diff --git a/gitnexus/src/core/ingestion/entry-point-scoring.ts b/gitnexus/src/core/ingestion/entry-point-scoring.ts index f8d85ae48..58cf9389c 100644 --- a/gitnexus/src/core/ingestion/entry-point-scoring.ts +++ b/gitnexus/src/core/ingestion/entry-point-scoring.ts @@ -12,18 +12,15 @@ import { detectFrameworkFromPath } from './framework-detection.js'; import { SupportedLanguages } from 'gitnexus-shared'; +import { providers } from './languages/index.js'; // ============================================================================ -// NAME PATTERNS - All 13 supported languages +// NAME PATTERNS // ============================================================================ /** - * Common entry point naming patterns by language. - * These patterns indicate functions that are likely feature entry points. - * - * Universal patterns are separated from per-language patterns so the per-language - * table can use `satisfies Record` for compile-time - * exhaustiveness — the compiler catches any missing language entry. + * Universal entry point naming patterns shared across all languages. + * Per-language patterns live on each LanguageProvider.entryPointPatterns. */ const UNIVERSAL_ENTRY_POINT_PATTERNS: RegExp[] = [ /^(main|init|bootstrap|start|run|setup|configure)$/i, @@ -40,201 +37,11 @@ const UNIVERSAL_ENTRY_POINT_PATTERNS: RegExp[] = [ /^emit[A-Z]/, // emitEvent ]; -export const ENTRY_POINT_PATTERNS = { - // JavaScript/TypeScript - [SupportedLanguages.JavaScript]: [ - /^use[A-Z]/, // React hooks (useEffect, etc.) - ], - [SupportedLanguages.TypeScript]: [ - /^use[A-Z]/, // React hooks - ], - - // Python - [SupportedLanguages.Python]: [ - /^app$/, // Flask/FastAPI app - /^(get|post|put|delete|patch)_/i, // REST conventions - /^api_/, // API functions - /^view_/, // Django views - ], - - // Java - [SupportedLanguages.Java]: [ - /^do[A-Z]/, // doGet, doPost (Servlets) - /^create[A-Z]/, // Factory patterns - /^build[A-Z]/, // Builder patterns - /Service$/, // UserService - ], - - // Kotlin - [SupportedLanguages.Kotlin]: [ - /^on(Create|Start|Resume|Pause|Stop|Destroy)$/, // Android lifecycle - /^do[A-Z]/, // doGet, doPost (shared JVM Servlet pattern) - /^create[A-Z]/, // Factory patterns - /^build[A-Z]/, // Builder patterns - /ViewModel$/, // MVVM pattern (Android) - /^module$/, // Ktor module entry point - /Service$/, // Service classes - ], - - // C# - [SupportedLanguages.CSharp]: [ - /^(Get|Post|Put|Delete|Patch)/, // ASP.NET action methods - /Action$/, // MVC actions - /^On[A-Z]/, // Event handlers / Blazor lifecycle - /Async$/, // Async entry points - /^Configure$/, // Startup.Configure - /^ConfigureServices$/, // Startup.ConfigureServices - /^Handle$/, // MediatR / generic handler - /^Execute$/, // Command pattern - /^Invoke$/, // Middleware Invoke - /^Map[A-Z]/, // Minimal API MapGet, MapPost - /Service$/, // Service classes - /^Seed/, // Database seeding - ], - - // Go - [SupportedLanguages.Go]: [ - /Handler$/, // http.Handler pattern - /^Serve/, // ServeHTTP - /^New[A-Z]/, // Constructor pattern (returns new instance) - /^Make[A-Z]/, // Make functions - ], - - // Rust - [SupportedLanguages.Rust]: [ - /^(get|post|put|delete)_handler$/i, - /^handle_/, // handle_request - /^new$/, // Constructor pattern - /^run$/, // run entry point - /^spawn/, // Async spawn - ], - - // C - explicit main() boost plus common C entry point conventions - [SupportedLanguages.C]: [ - /^main$/, // THE entry point - /^init_/, // init_server, init_client - /_init$/, // module_init, server_init - /^start_/, // start_server - /_start$/, // thread_start - /^run_/, // run_loop - /_run$/, // event_run - /^stop_/, // stop_server - /_stop$/, // service_stop - /^open_/, // open_connection - /_open$/, // file_open - /^close_/, // close_connection - /_close$/, // socket_close - /^create_/, // create_session - /_create$/, // object_create - /^destroy_/, // destroy_session - /_destroy$/, // object_destroy - /^handle_/, // handle_request - /_handler$/, // signal_handler - /_callback$/, // event_callback - /^cmd_/, // tmux: cmd_new_window, cmd_attach_session - /^server_/, // server_start, server_loop - /^client_/, // client_connect - /^session_/, // session_create - /^window_/, // window_resize (tmux) - /^key_/, // key_press - /^input_/, // input_parse - /^output_/, // output_write - /^notify_/, // notify_client - /^control_/, // control_start - ], - - // C++ - same as C plus OOP/template patterns - [SupportedLanguages.CPlusPlus]: [ - /^main$/, // THE entry point - /^init_/, - /_init$/, - /^Create[A-Z]/, // Factory patterns - /^create_/, - /^Run$/, // Run methods - /^run$/, - /^Start$/, // Start methods - /^start$/, - /^handle_/, - /_handler$/, - /_callback$/, - /^OnEvent/, // Event callbacks - /^on_/, - /::Run$/, // Class::Run - /::Start$/, // Class::Start - /::Init$/, // Class::Init - /::Execute$/, // Class::Execute - ], - - // Swift / iOS - [SupportedLanguages.Swift]: [ - /^viewDidLoad$/, // UIKit lifecycle - /^viewWillAppear$/, // UIKit lifecycle - /^viewDidAppear$/, // UIKit lifecycle - /^viewWillDisappear$/, // UIKit lifecycle - /^viewDidDisappear$/, // UIKit lifecycle - /^application\(/, // AppDelegate methods - /^scene\(/, // SceneDelegate methods - /^body$/, // SwiftUI View.body - /Coordinator$/, // Coordinator pattern - /^sceneDidBecomeActive$/, // SceneDelegate lifecycle - /^sceneWillResignActive$/, // SceneDelegate lifecycle - /^didFinishLaunchingWithOptions$/, // AppDelegate - /ViewController$/, // ViewController classes - /^configure[A-Z]/, // Configuration methods - /^setup[A-Z]/, // Setup methods - /^makeBody$/, // SwiftUI ViewModifier - ], - - // PHP / Laravel - [SupportedLanguages.PHP]: [ - /Controller$/, // UserController (class name convention) - /^handle$/, // Job::handle(), Listener::handle() - /^execute$/, // Command::execute() - /^boot$/, // ServiceProvider::boot() - /^register$/, // ServiceProvider::register() - /^__invoke$/, // Invokable controllers/actions - /^(index|show|store|update|destroy|create|edit)$/, // RESTful resource methods - /^(get|post|put|delete|patch)[A-Z]/, // Explicit HTTP method actions - /^run$/, // Command/Job run() - /^fire$/, // Event fire() - /^dispatch$/, // Dispatchable jobs - /Service$/, // UserService (Service layer) - /Repository$/, // UserRepository (Repository pattern) - /^find$/, // Repository::find() - /^findAll$/, // Repository::findAll() - /^save$/, // Repository::save() - /^delete$/, // Repository::delete() - ], - - // Ruby - [SupportedLanguages.Ruby]: [ - /^call$/, // Service objects (MyService.call) - /^perform$/, // Background jobs (Sidekiq, ActiveJob) - /^execute$/, // Command pattern - ], - - // Dart / Flutter - [SupportedLanguages.Dart]: [ - /^main$/, // App entry - /^build$/, // Widget.build — fundamental Flutter render entry point - /^createState$/, // StatefulWidget.createState - /^initState$/, // State lifecycle initialization - /^dispose$/, // State lifecycle teardown - /^didChangeDependencies$/, // State lifecycle — InheritedWidget changes - /^didUpdateWidget$/, // State lifecycle — widget rebuild with new config - /^runApp$/, // App entry point - /^onEvent$/, // BLoC event handler - /^mapEventToState$/, // Legacy BLoC pattern - ], - [SupportedLanguages.Vue]: [], // Vue uses TypeScript queries — entry points handled via TS patterns - [SupportedLanguages.Cobol]: [], // Standalone regex processor — no tree-sitter entry points -} satisfies Record; - -/** Pre-computed merged patterns (universal + language-specific) to avoid per-call array allocation. */ +/** Pre-computed merged patterns (universal + language-specific) from providers. */ const MERGED_ENTRY_POINT_PATTERNS = Object.fromEntries( - Object.values(SupportedLanguages).map((lang) => [ + Object.entries(providers).map(([lang, provider]) => [ lang, - [...UNIVERSAL_ENTRY_POINT_PATTERNS, ...(ENTRY_POINT_PATTERNS[lang] ?? [])], + [...UNIVERSAL_ENTRY_POINT_PATTERNS, ...(provider.entryPointPatterns ?? [])], ]), ) as Record; diff --git a/gitnexus/src/core/ingestion/framework-detection.ts b/gitnexus/src/core/ingestion/framework-detection.ts index 739f22967..fca116c22 100644 --- a/gitnexus/src/core/ingestion/framework-detection.ts +++ b/gitnexus/src/core/ingestion/framework-detection.ts @@ -11,6 +11,8 @@ */ import { SupportedLanguages } from 'gitnexus-shared'; +import type { AstFrameworkPatternConfig } from './language-provider.js'; +import { providers } from './languages/index.js'; // ============================================================================ // TYPES @@ -518,395 +520,14 @@ export function detectFrameworkFromPath(filePath: string): FrameworkHint | null // AST-BASED FRAMEWORK DETECTION // ============================================================================ -/** - * Patterns that indicate framework entry points within code definitions. - * These are matched against AST node text (class/method/function declaration text). - */ -export const FRAMEWORK_AST_PATTERNS = { - // JavaScript/TypeScript decorators - nestjs: ['@Controller', '@Get', '@Post', '@Put', '@Delete', '@Patch'], - 'expo-router': [ - 'router.push', - 'router.replace', - 'router.navigate', - 'useRouter', - 'useLocalSearchParams', - 'useSegments', - 'expo-router', - ], - express: ['app.get', 'app.post', 'app.put', 'app.delete', 'router.get', 'router.post'], - - // Python decorators - fastapi: ['@app.get', '@app.post', '@app.put', '@app.delete', '@router.get'], - flask: ['@app.route', '@blueprint.route'], - - // Java annotations - spring: ['@RestController', '@Controller', '@GetMapping', '@PostMapping', '@RequestMapping'], - jaxrs: ['@Path', '@GET', '@POST', '@PUT', '@DELETE'], - - // C# attributes - aspnet: [ - '[ApiController]', - '[HttpGet]', - '[HttpPost]', - '[HttpPut]', - '[HttpDelete]', - '[Route]', - '[Authorize]', - '[AllowAnonymous]', - ], - signalr: ['[HubMethodName]', ': Hub', ': Hub<'], - blazor: ['@page', '[Parameter]', '@inject'], - efcore: ['DbContext', 'DbSet<', 'OnModelCreating'], - - // Go patterns (function signatures include framework types) - 'go-http': [ - 'http.Handler', - 'http.HandlerFunc', - 'ServeHTTP', - 'http.ResponseWriter', - 'http.Request', - ], - gin: ['gin.Context', 'gin.Default', 'gin.New'], - echo: ['echo.Context', 'echo.New'], - fiber: ['fiber.Ctx', 'fiber.New', 'fiber.App'], - 'go-grpc': ['grpc.Server', 'RegisterServer', 'pb.Unimplemented'], - - // ORM patterns - prisma: ['prisma.', 'PrismaClient', '@prisma/client'], - supabase: ['supabase.from', 'createClient', '@supabase/supabase-js'], - - // PHP/Laravel - laravel: [ - 'Route::get', - 'Route::post', - 'Route::put', - 'Route::delete', - 'Route::resource', - 'Route::apiResource', - '#[Route(', - ], - - // Rust macros (proc-macro attributes in definition text) - actix: ['#[get', '#[post', '#[put', '#[delete', '#[actix_web', 'HttpRequest', 'HttpResponse'], - axum: ['Router::new', 'axum::extract', 'axum::routing'], - rocket: ['#[get', '#[post', '#[launch', 'rocket::'], - tokio: ['#[tokio::main]', '#[tokio::test]'], - - // C++ patterns (Qt, Boost) - qt: [ - 'Q_OBJECT', - 'Q_INVOKABLE', - 'Q_PROPERTY', - 'Q_SIGNALS', - 'Q_SLOTS', - 'Q_SIGNAL', - 'Q_SLOT', - 'QWidget', - 'QApplication', - ], - - // Swift/iOS - uikit: [ - 'viewDidLoad', - 'viewWillAppear', - 'viewDidAppear', - 'UIViewController', - '@IBOutlet', - '@IBAction', - '@objc', - ], - swiftui: [ - '@main', - 'WindowGroup', - 'ContentView', - '@StateObject', - '@ObservedObject', - '@EnvironmentObject', - '@Published', - ], - vapor: ['app.get', 'app.post', 'req.content.decode', 'Vapor'], - - // Ruby patterns (class-level macros in definition text) - rails: [ - 'ApplicationController', - 'ApplicationRecord', - 'ActiveRecord::Base', - 'before_action', - 'after_action', - 'has_many', - 'belongs_to', - 'has_one', - 'validates', - ], - sinatra: ['Sinatra::Base', 'Sinatra::Application'], - - // Dart/Flutter - flutter: [ - 'StatelessWidget', - 'StatefulWidget', - 'BuildContext', - 'Widget build', - 'ChangeNotifier', - 'GetxController', - 'Cubit<', - 'Bloc<', - 'ConsumerWidget', - ], - riverpod: ['@riverpod', 'ref.watch', 'ref.read', 'AsyncNotifier', 'Notifier'], -}; - -interface AstFrameworkPatternConfig { - framework: string; - entryPointMultiplier: number; - reason: string; - patterns: string[]; -} - -export const AST_FRAMEWORK_PATTERNS_BY_LANGUAGE = { - [SupportedLanguages.JavaScript]: [ - { - framework: 'nestjs', - entryPointMultiplier: 3.2, - reason: 'nestjs-decorator', - patterns: FRAMEWORK_AST_PATTERNS.nestjs, - }, - { - framework: 'expo-router', - entryPointMultiplier: 2.5, - reason: 'expo-router-navigation', - patterns: FRAMEWORK_AST_PATTERNS['expo-router'], - }, - ], - [SupportedLanguages.TypeScript]: [ - { - framework: 'nestjs', - entryPointMultiplier: 3.2, - reason: 'nestjs-decorator', - patterns: FRAMEWORK_AST_PATTERNS.nestjs, - }, - { - framework: 'expo-router', - entryPointMultiplier: 2.5, - reason: 'expo-router-navigation', - patterns: FRAMEWORK_AST_PATTERNS['expo-router'], - }, - ], - [SupportedLanguages.Python]: [ - { - framework: 'fastapi', - entryPointMultiplier: 3.0, - reason: 'fastapi-decorator', - patterns: FRAMEWORK_AST_PATTERNS.fastapi, - }, - { - framework: 'flask', - entryPointMultiplier: 2.8, - reason: 'flask-decorator', - patterns: FRAMEWORK_AST_PATTERNS.flask, - }, - ], - [SupportedLanguages.Java]: [ - { - framework: 'spring', - entryPointMultiplier: 3.2, - reason: 'spring-annotation', - patterns: FRAMEWORK_AST_PATTERNS.spring, - }, - { - framework: 'jaxrs', - entryPointMultiplier: 3.0, - reason: 'jaxrs-annotation', - patterns: FRAMEWORK_AST_PATTERNS.jaxrs, - }, - ], - [SupportedLanguages.Kotlin]: [ - { - framework: 'spring-kotlin', - entryPointMultiplier: 3.2, - reason: 'spring-kotlin-annotation', - patterns: FRAMEWORK_AST_PATTERNS.spring, - }, - { - framework: 'jaxrs', - entryPointMultiplier: 3.0, - reason: 'jaxrs-annotation', - patterns: FRAMEWORK_AST_PATTERNS.jaxrs, - }, - { - framework: 'ktor', - entryPointMultiplier: 2.8, - reason: 'ktor-routing', - patterns: ['routing', 'embeddedServer', 'Application.module'], - }, - { - framework: 'android-kotlin', - entryPointMultiplier: 2.5, - reason: 'android-annotation', - patterns: ['@AndroidEntryPoint', 'AppCompatActivity', 'Fragment('], - }, - ], - [SupportedLanguages.CSharp]: [ - { - framework: 'aspnet', - entryPointMultiplier: 3.2, - reason: 'aspnet-attribute', - patterns: FRAMEWORK_AST_PATTERNS.aspnet, - }, - { - framework: 'signalr', - entryPointMultiplier: 2.8, - reason: 'signalr-attribute', - patterns: FRAMEWORK_AST_PATTERNS.signalr, - }, - { - framework: 'blazor', - entryPointMultiplier: 2.5, - reason: 'blazor-attribute', - patterns: FRAMEWORK_AST_PATTERNS.blazor, - }, - { - framework: 'efcore', - entryPointMultiplier: 2.0, - reason: 'efcore-pattern', - patterns: FRAMEWORK_AST_PATTERNS.efcore, - }, - ], - [SupportedLanguages.PHP]: [ - { - framework: 'laravel', - entryPointMultiplier: 3.0, - reason: 'php-route-attribute', - patterns: FRAMEWORK_AST_PATTERNS.laravel, - }, - ], - [SupportedLanguages.Go]: [ - { - framework: 'go-http', - entryPointMultiplier: 2.5, - reason: 'go-http-handler', - patterns: FRAMEWORK_AST_PATTERNS['go-http'], - }, - { - framework: 'gin', - entryPointMultiplier: 3.0, - reason: 'gin-handler', - patterns: FRAMEWORK_AST_PATTERNS.gin, - }, - { - framework: 'echo', - entryPointMultiplier: 3.0, - reason: 'echo-handler', - patterns: FRAMEWORK_AST_PATTERNS.echo, - }, - { - framework: 'fiber', - entryPointMultiplier: 3.0, - reason: 'fiber-handler', - patterns: FRAMEWORK_AST_PATTERNS.fiber, - }, - { - framework: 'go-grpc', - entryPointMultiplier: 2.8, - reason: 'grpc-service', - patterns: FRAMEWORK_AST_PATTERNS['go-grpc'], - }, - ], - [SupportedLanguages.Rust]: [ - { - framework: 'actix-web', - entryPointMultiplier: 3.0, - reason: 'actix-attribute', - patterns: FRAMEWORK_AST_PATTERNS.actix, - }, - { - framework: 'axum', - entryPointMultiplier: 3.0, - reason: 'axum-routing', - patterns: FRAMEWORK_AST_PATTERNS.axum, - }, - { - framework: 'rocket', - entryPointMultiplier: 3.0, - reason: 'rocket-attribute', - patterns: FRAMEWORK_AST_PATTERNS.rocket, - }, - { - framework: 'tokio', - entryPointMultiplier: 2.5, - reason: 'tokio-runtime', - patterns: FRAMEWORK_AST_PATTERNS.tokio, - }, - ], - [SupportedLanguages.C]: [], // C has no framework-specific AST patterns (POSIX/socket patterns are in entry-point-scoring) - [SupportedLanguages.CPlusPlus]: [ - { - framework: 'qt', - entryPointMultiplier: 2.8, - reason: 'qt-macro', - patterns: FRAMEWORK_AST_PATTERNS.qt, - }, - ], - [SupportedLanguages.Swift]: [ - { - framework: 'uikit', - entryPointMultiplier: 2.5, - reason: 'uikit-lifecycle', - patterns: FRAMEWORK_AST_PATTERNS.uikit, - }, - { - framework: 'swiftui', - entryPointMultiplier: 2.8, - reason: 'swiftui-pattern', - patterns: FRAMEWORK_AST_PATTERNS.swiftui, - }, - { - framework: 'vapor', - entryPointMultiplier: 3.0, - reason: 'vapor-routing', - patterns: FRAMEWORK_AST_PATTERNS.vapor, - }, - ], - [SupportedLanguages.Ruby]: [ - { - framework: 'rails', - entryPointMultiplier: 3.0, - reason: 'rails-pattern', - patterns: FRAMEWORK_AST_PATTERNS.rails, - }, - { - framework: 'sinatra', - entryPointMultiplier: 2.8, - reason: 'sinatra-pattern', - patterns: FRAMEWORK_AST_PATTERNS.sinatra, - }, - ], - [SupportedLanguages.Dart]: [ - { - framework: 'flutter', - entryPointMultiplier: 2.5, - reason: 'flutter-widget', - patterns: FRAMEWORK_AST_PATTERNS.flutter, - }, - { - framework: 'riverpod', - entryPointMultiplier: 2.8, - reason: 'riverpod-pattern', - patterns: FRAMEWORK_AST_PATTERNS.riverpod, - }, - ], - [SupportedLanguages.Vue]: [], // Vue uses TypeScript AST framework detection - [SupportedLanguages.Cobol]: [], // Standalone regex processor — no AST framework patterns -} satisfies Record; - -/** Pre-lowercased patterns for O(1) pattern matching at runtime */ -const AST_PATTERNS_LOWERED: Record< - string, - Array<{ framework: string; entryPointMultiplier: number; reason: string; patterns: string[] }> -> = Object.fromEntries( - Object.entries(AST_FRAMEWORK_PATTERNS_BY_LANGUAGE).map(([lang, cfgs]) => [ +/** Pre-lowercased patterns for O(1) pattern matching at runtime — built from providers. */ +const AST_PATTERNS_LOWERED: Record = Object.fromEntries( + Object.entries(providers).map(([lang, provider]) => [ lang, - cfgs.map((cfg) => ({ ...cfg, patterns: cfg.patterns.map((p) => p.toLowerCase()) })), + (provider.astFrameworkPatterns ?? []).map((cfg) => ({ + ...cfg, + patterns: cfg.patterns.map((p) => p.toLowerCase()), + })), ]), ); diff --git a/gitnexus/src/core/ingestion/language-provider.ts b/gitnexus/src/core/ingestion/language-provider.ts index 92566d30e..12ce0839b 100644 --- a/gitnexus/src/core/ingestion/language-provider.ts +++ b/gitnexus/src/core/ingestion/language-provider.ts @@ -78,6 +78,14 @@ export type ImportSemantics = | 'namespace' | 'explicit-reexport'; +/** Configuration for AST-based framework detection patterns. */ +export interface AstFrameworkPatternConfig { + framework: string; + entryPointMultiplier: number; + reason: string; + patterns: string[]; +} + /** * Everything a language needs to provide. * Required fields must be explicitly set; optional fields have defaults @@ -89,6 +97,16 @@ interface LanguageProviderConfig { /** File extensions that map to this language (e.g., ['.ts', '.tsx']) */ readonly extensions: readonly string[]; + /** Entry-point function name patterns specific to this language. + * Merged with universal patterns at runtime for process detection scoring. + * Default: [] (only universal patterns apply). */ + readonly entryPointPatterns?: readonly RegExp[]; + + /** AST-based framework detection patterns for this language. + * Used by detectFrameworkFromAST to identify framework entry points. + * Default: [] (no AST framework detection for this language). */ + readonly astFrameworkPatterns?: readonly AstFrameworkPatternConfig[]; + // ── Parser ──────────────────────────────────────────────────────── /** Parse strategy: 'tree-sitter' (default) uses AST parsing via tree-sitter. * 'standalone' means the language has its own regex-based processor and diff --git a/gitnexus/src/core/ingestion/languages/c-cpp.ts b/gitnexus/src/core/ingestion/languages/c-cpp.ts index 37e6da732..907b1da6d 100644 --- a/gitnexus/src/core/ingestion/languages/c-cpp.ts +++ b/gitnexus/src/core/ingestion/languages/c-cpp.ts @@ -12,6 +12,7 @@ import { SupportedLanguages } from 'gitnexus-shared'; import { createClassExtractor } from '../class-extractors/generic.js'; import { cClassConfig, cppClassConfig } from '../class-extractors/configs/c-cpp.js'; import { defineLanguage } from '../language-provider.js'; +import type { AstFrameworkPatternConfig } from '../language-provider.js'; import { typeConfig as cCppConfig } from '../type-extractors/c-cpp.js'; import { cCppExportChecker } from '../export-detection.js'; import { createImportResolver } from '../import-resolvers/resolver-factory.js'; @@ -317,6 +318,38 @@ const cppLabelOverride: NonNullable = ( export const cProvider = defineLanguage({ id: SupportedLanguages.C, extensions: ['.c'], + entryPointPatterns: [ + /^main$/, + /^init_/, + /_init$/, + /^start_/, + /_start$/, + /^run_/, + /_run$/, + /^stop_/, + /_stop$/, + /^open_/, + /_open$/, + /^close_/, + /_close$/, + /^create_/, + /_create$/, + /^destroy_/, + /_destroy$/, + /^handle_/, + /_handler$/, + /_callback$/, + /^cmd_/, + /^server_/, + /^client_/, + /^session_/, + /^window_/, + /^key_/, + /^input_/, + /^output_/, + /^notify_/, + /^control_/, + ], treeSitterQueries: C_QUERIES, typeConfig: cCppConfig, exportChecker: cCppExportChecker, @@ -338,6 +371,44 @@ export const cProvider = defineLanguage({ export const cppProvider = defineLanguage({ id: SupportedLanguages.CPlusPlus, extensions: ['.cpp', '.cc', '.cxx', '.h', '.hpp', '.hxx', '.hh'], + entryPointPatterns: [ + /^main$/, + /^init_/, + /_init$/, + /^Create[A-Z]/, + /^create_/, + /^Run$/, + /^run$/, + /^Start$/, + /^start$/, + /^handle_/, + /_handler$/, + /_callback$/, + /^OnEvent/, + /^on_/, + /::Run$/, + /::Start$/, + /::Init$/, + /::Execute$/, + ], + astFrameworkPatterns: [ + { + framework: 'qt', + entryPointMultiplier: 2.8, + reason: 'qt-macro', + patterns: [ + 'Q_OBJECT', + 'Q_INVOKABLE', + 'Q_PROPERTY', + 'Q_SIGNALS', + 'Q_SLOTS', + 'Q_SIGNAL', + 'Q_SLOT', + 'QWidget', + 'QApplication', + ], + }, + ], treeSitterQueries: CPP_QUERIES, typeConfig: cCppConfig, exportChecker: cCppExportChecker, diff --git a/gitnexus/src/core/ingestion/languages/cobol.ts b/gitnexus/src/core/ingestion/languages/cobol.ts index 78ee51ecc..6e54c2309 100644 --- a/gitnexus/src/core/ingestion/languages/cobol.ts +++ b/gitnexus/src/core/ingestion/languages/cobol.ts @@ -16,6 +16,8 @@ export const cobolProvider = defineLanguage({ id: SupportedLanguages.Cobol, parseStrategy: 'standalone', extensions: [], // COBOL files detected by cobol-processor's isCobolFile/isJclFile + entryPointPatterns: [], + astFrameworkPatterns: [], treeSitterQueries: '', typeConfig: { declarationNodeTypes: new Set(), diff --git a/gitnexus/src/core/ingestion/languages/csharp.ts b/gitnexus/src/core/ingestion/languages/csharp.ts index 837a7517b..32bf08779 100644 --- a/gitnexus/src/core/ingestion/languages/csharp.ts +++ b/gitnexus/src/core/ingestion/languages/csharp.ts @@ -16,6 +16,7 @@ import { createImportResolver } from '../import-resolvers/resolver-factory.js'; import { csharpImportConfig } from '../import-resolvers/configs/csharp.js'; import { extractCSharpNamedBindings } from '../named-bindings/csharp.js'; import { CSHARP_QUERIES } from '../tree-sitter-queries.js'; +import type { AstFrameworkPatternConfig } from '../language-provider.js'; import { createCallExtractor } from '../call-extractors/generic.js'; import { csharpCallConfig } from '../call-extractors/configs/csharp.js'; import { createFieldExtractor } from '../field-extractors/generic.js'; @@ -135,6 +136,55 @@ const BUILT_INS: ReadonlySet = new Set([ export const csharpProvider = defineLanguage({ id: SupportedLanguages.CSharp, extensions: ['.cs'], + entryPointPatterns: [ + /^(Get|Post|Put|Delete|Patch)/, + /Action$/, + /^On[A-Z]/, + /Async$/, + /^Configure$/, + /^ConfigureServices$/, + /^Handle$/, + /^Execute$/, + /^Invoke$/, + /^Map[A-Z]/, + /Service$/, + /^Seed/, + ], + astFrameworkPatterns: [ + { + framework: 'aspnet', + entryPointMultiplier: 3.2, + reason: 'aspnet-attribute', + patterns: [ + '[ApiController]', + '[HttpGet]', + '[HttpPost]', + '[HttpPut]', + '[HttpDelete]', + '[Route]', + '[Authorize]', + '[AllowAnonymous]', + ], + }, + { + framework: 'signalr', + entryPointMultiplier: 2.8, + reason: 'signalr-attribute', + patterns: ['[HubMethodName]', ': Hub', ': Hub<'], + }, + { + framework: 'blazor', + entryPointMultiplier: 2.5, + reason: 'blazor-attribute', + patterns: ['@page', '[Parameter]', '@inject'], + }, + { + framework: 'efcore', + entryPointMultiplier: 2.0, + reason: 'efcore-pattern', + patterns: ['DbContext', 'DbSet<', 'OnModelCreating'], + }, + ] satisfies AstFrameworkPatternConfig[], treeSitterQueries: CSHARP_QUERIES, typeConfig: csharpConfig, exportChecker: csharpExportChecker, diff --git a/gitnexus/src/core/ingestion/languages/dart.ts b/gitnexus/src/core/ingestion/languages/dart.ts index 130d15deb..29a63fd3a 100644 --- a/gitnexus/src/core/ingestion/languages/dart.ts +++ b/gitnexus/src/core/ingestion/languages/dart.ts @@ -17,6 +17,7 @@ import { SupportedLanguages } from 'gitnexus-shared'; import { createClassExtractor } from '../class-extractors/generic.js'; import { dartClassConfig } from '../class-extractors/configs/dart.js'; import { defineLanguage } from '../language-provider.js'; +import type { AstFrameworkPatternConfig } from '../language-provider.js'; import { typeConfig as dartConfig } from '../type-extractors/dart.js'; import { dartExportChecker } from '../export-detection.js'; import { createImportResolver } from '../import-resolvers/resolver-factory.js'; @@ -93,6 +94,42 @@ const BUILT_INS: ReadonlySet = new Set([ export const dartProvider = defineLanguage({ id: SupportedLanguages.Dart, extensions: ['.dart'], + entryPointPatterns: [ + /^main$/, + /^build$/, + /^createState$/, + /^initState$/, + /^dispose$/, + /^didChangeDependencies$/, + /^didUpdateWidget$/, + /^runApp$/, + /^onEvent$/, + /^mapEventToState$/, + ], + astFrameworkPatterns: [ + { + framework: 'flutter', + entryPointMultiplier: 2.5, + reason: 'flutter-widget', + patterns: [ + 'StatelessWidget', + 'StatefulWidget', + 'BuildContext', + 'Widget build', + 'ChangeNotifier', + 'GetxController', + 'Cubit<', + 'Bloc<', + 'ConsumerWidget', + ], + }, + { + framework: 'riverpod', + entryPointMultiplier: 2.8, + reason: 'riverpod-pattern', + patterns: ['@riverpod', 'ref.watch', 'ref.read', 'AsyncNotifier', 'Notifier'], + }, + ], treeSitterQueries: DART_QUERIES, typeConfig: dartConfig, exportChecker: dartExportChecker, diff --git a/gitnexus/src/core/ingestion/languages/go.ts b/gitnexus/src/core/ingestion/languages/go.ts index 245dc197b..64b9e359a 100644 --- a/gitnexus/src/core/ingestion/languages/go.ts +++ b/gitnexus/src/core/ingestion/languages/go.ts @@ -18,6 +18,7 @@ import { goExportChecker } from '../export-detection.js'; import { createImportResolver } from '../import-resolvers/resolver-factory.js'; import { goImportConfig } from '../import-resolvers/configs/go.js'; import { GO_QUERIES } from '../tree-sitter-queries.js'; +import type { AstFrameworkPatternConfig } from '../language-provider.js'; import { createFieldExtractor } from '../field-extractors/generic.js'; import { goConfig as goFieldConfig } from '../field-extractors/configs/go.js'; import { createMethodExtractor } from '../method-extractors/generic.js'; @@ -32,6 +33,45 @@ import { goHeritageConfig } from '../heritage-extractors/configs/go.js'; export const goProvider = defineLanguage({ id: SupportedLanguages.Go, extensions: ['.go'], + entryPointPatterns: [/Handler$/, /^Serve/, /^New[A-Z]/, /^Make[A-Z]/], + astFrameworkPatterns: [ + { + framework: 'go-http', + entryPointMultiplier: 2.5, + reason: 'go-http-handler', + patterns: [ + 'http.Handler', + 'http.HandlerFunc', + 'ServeHTTP', + 'http.ResponseWriter', + 'http.Request', + ], + }, + { + framework: 'gin', + entryPointMultiplier: 3.0, + reason: 'gin-handler', + patterns: ['gin.Context', 'gin.Default', 'gin.New'], + }, + { + framework: 'echo', + entryPointMultiplier: 3.0, + reason: 'echo-handler', + patterns: ['echo.Context', 'echo.New'], + }, + { + framework: 'fiber', + entryPointMultiplier: 3.0, + reason: 'fiber-handler', + patterns: ['fiber.Ctx', 'fiber.New', 'fiber.App'], + }, + { + framework: 'go-grpc', + entryPointMultiplier: 2.8, + reason: 'grpc-service', + patterns: ['grpc.Server', 'RegisterServer', 'pb.Unimplemented'], + }, + ] satisfies AstFrameworkPatternConfig[], treeSitterQueries: GO_QUERIES, typeConfig: goConfig, exportChecker: goExportChecker, diff --git a/gitnexus/src/core/ingestion/languages/java.ts b/gitnexus/src/core/ingestion/languages/java.ts index 4781219d3..96139ccda 100644 --- a/gitnexus/src/core/ingestion/languages/java.ts +++ b/gitnexus/src/core/ingestion/languages/java.ts @@ -11,6 +11,7 @@ import { SupportedLanguages } from 'gitnexus-shared'; import { createClassExtractor } from '../class-extractors/generic.js'; import { javaClassConfig } from '../class-extractors/configs/jvm.js'; import { defineLanguage } from '../language-provider.js'; +import type { AstFrameworkPatternConfig } from '../language-provider.js'; import { javaTypeConfig } from '../type-extractors/jvm.js'; import { javaExportChecker } from '../export-detection.js'; import { createImportResolver } from '../import-resolvers/resolver-factory.js'; @@ -30,6 +31,27 @@ import { createHeritageExtractor } from '../heritage-extractors/generic.js'; export const javaProvider = defineLanguage({ id: SupportedLanguages.Java, extensions: ['.java'], + entryPointPatterns: [/^do[A-Z]/, /^create[A-Z]/, /^build[A-Z]/, /Service$/], + astFrameworkPatterns: [ + { + framework: 'spring', + entryPointMultiplier: 3.2, + reason: 'spring-annotation', + patterns: [ + '@RestController', + '@Controller', + '@GetMapping', + '@PostMapping', + '@RequestMapping', + ], + }, + { + framework: 'jaxrs', + entryPointMultiplier: 3.0, + reason: 'jaxrs-annotation', + patterns: ['@Path', '@GET', '@POST', '@PUT', '@DELETE'], + }, + ] satisfies AstFrameworkPatternConfig[], treeSitterQueries: JAVA_QUERIES, typeConfig: javaTypeConfig, exportChecker: javaExportChecker, diff --git a/gitnexus/src/core/ingestion/languages/kotlin.ts b/gitnexus/src/core/ingestion/languages/kotlin.ts index dd65e5d42..78d8726e6 100644 --- a/gitnexus/src/core/ingestion/languages/kotlin.ts +++ b/gitnexus/src/core/ingestion/languages/kotlin.ts @@ -18,6 +18,7 @@ import { kotlinImportConfig } from '../import-resolvers/configs/jvm.js'; import { extractKotlinNamedBindings } from '../named-bindings/kotlin.js'; import { appendKotlinWildcard } from '../import-resolvers/jvm.js'; import { KOTLIN_QUERIES } from '../tree-sitter-queries.js'; +import type { AstFrameworkPatternConfig } from '../language-provider.js'; import type { SyntaxNode } from '../utils/ast-helpers.js'; import { createCallExtractor } from '../call-extractors/generic.js'; import { kotlinCallConfig } from '../call-extractors/configs/jvm.js'; @@ -105,6 +106,47 @@ const BUILT_INS: ReadonlySet = new Set([ export const kotlinProvider = defineLanguage({ id: SupportedLanguages.Kotlin, extensions: ['.kt', '.kts'], + entryPointPatterns: [ + /^on(Create|Start|Resume|Pause|Stop|Destroy)$/, + /^do[A-Z]/, + /^create[A-Z]/, + /^build[A-Z]/, + /ViewModel$/, + /^module$/, + /Service$/, + ], + astFrameworkPatterns: [ + { + framework: 'spring-kotlin', + entryPointMultiplier: 3.2, + reason: 'spring-kotlin-annotation', + patterns: [ + '@RestController', + '@Controller', + '@GetMapping', + '@PostMapping', + '@RequestMapping', + ], + }, + { + framework: 'jaxrs', + entryPointMultiplier: 3.0, + reason: 'jaxrs-annotation', + patterns: ['@Path', '@GET', '@POST', '@PUT', '@DELETE'], + }, + { + framework: 'ktor', + entryPointMultiplier: 2.8, + reason: 'ktor-routing', + patterns: ['routing', 'embeddedServer', 'Application.module'], + }, + { + framework: 'android-kotlin', + entryPointMultiplier: 2.5, + reason: 'android-annotation', + patterns: ['@AndroidEntryPoint', 'AppCompatActivity', 'Fragment('], + }, + ] satisfies AstFrameworkPatternConfig[], treeSitterQueries: KOTLIN_QUERIES, typeConfig: kotlinTypeConfig, exportChecker: kotlinExportChecker, diff --git a/gitnexus/src/core/ingestion/languages/php.ts b/gitnexus/src/core/ingestion/languages/php.ts index 736a63468..3689f297f 100644 --- a/gitnexus/src/core/ingestion/languages/php.ts +++ b/gitnexus/src/core/ingestion/languages/php.ts @@ -10,6 +10,7 @@ import { SupportedLanguages } from 'gitnexus-shared'; import { createClassExtractor } from '../class-extractors/generic.js'; import { phpClassConfig } from '../class-extractors/configs/php.js'; import { defineLanguage } from '../language-provider.js'; +import type { AstFrameworkPatternConfig } from '../language-provider.js'; import { typeConfig as phpConfig } from '../type-extractors/php.js'; import { phpExportChecker } from '../export-detection.js'; import { createImportResolver } from '../import-resolvers/resolver-factory.js'; @@ -239,6 +240,41 @@ function isPhpRouteFile(filePath: string): boolean { export const phpProvider = defineLanguage({ id: SupportedLanguages.PHP, extensions: ['.php', '.phtml', '.php3', '.php4', '.php5', '.php8'], + entryPointPatterns: [ + /Controller$/, + /^handle$/, + /^execute$/, + /^boot$/, + /^register$/, + /^__invoke$/, + /^(index|show|store|update|destroy|create|edit)$/, + /^(get|post|put|delete|patch)[A-Z]/, + /^run$/, + /^fire$/, + /^dispatch$/, + /Service$/, + /Repository$/, + /^find$/, + /^findAll$/, + /^save$/, + /^delete$/, + ], + astFrameworkPatterns: [ + { + framework: 'laravel', + entryPointMultiplier: 3.0, + reason: 'php-route-attribute', + patterns: [ + 'Route::get', + 'Route::post', + 'Route::put', + 'Route::delete', + 'Route::resource', + 'Route::apiResource', + '#[Route(', + ], + }, + ], treeSitterQueries: PHP_QUERIES, typeConfig: phpConfig, exportChecker: phpExportChecker, diff --git a/gitnexus/src/core/ingestion/languages/python.ts b/gitnexus/src/core/ingestion/languages/python.ts index 2b37aab1b..a0e834030 100644 --- a/gitnexus/src/core/ingestion/languages/python.ts +++ b/gitnexus/src/core/ingestion/languages/python.ts @@ -15,6 +15,7 @@ import { SupportedLanguages } from 'gitnexus-shared'; import { createClassExtractor } from '../class-extractors/generic.js'; import { pythonClassConfig } from '../class-extractors/configs/python.js'; import { defineLanguage } from '../language-provider.js'; +import type { AstFrameworkPatternConfig } from '../language-provider.js'; import { typeConfig as pythonConfig } from '../type-extractors/python.js'; import { pythonExportChecker } from '../export-detection.js'; import { createImportResolver } from '../import-resolvers/resolver-factory.js'; @@ -106,6 +107,21 @@ function normalizePythonStringLiteral(text: string): string | undefined { export const pythonProvider = defineLanguage({ id: SupportedLanguages.Python, extensions: ['.py'], + entryPointPatterns: [/^app$/, /^(get|post|put|delete|patch)_/i, /^api_/, /^view_/], + astFrameworkPatterns: [ + { + framework: 'fastapi', + entryPointMultiplier: 3.0, + reason: 'fastapi-decorator', + patterns: ['@app.get', '@app.post', '@app.put', '@app.delete', '@router.get'], + }, + { + framework: 'flask', + entryPointMultiplier: 2.8, + reason: 'flask-decorator', + patterns: ['@app.route', '@blueprint.route'], + }, + ] satisfies AstFrameworkPatternConfig[], treeSitterQueries: PYTHON_QUERIES, typeConfig: pythonConfig, exportChecker: pythonExportChecker, diff --git a/gitnexus/src/core/ingestion/languages/ruby.ts b/gitnexus/src/core/ingestion/languages/ruby.ts index e538f3fd2..6ea6f5b8b 100644 --- a/gitnexus/src/core/ingestion/languages/ruby.ts +++ b/gitnexus/src/core/ingestion/languages/ruby.ts @@ -12,6 +12,7 @@ import type { NodeLabel } from 'gitnexus-shared'; import { createClassExtractor } from '../class-extractors/generic.js'; import { rubyClassConfig } from '../class-extractors/configs/ruby.js'; import { defineLanguage } from '../language-provider.js'; +import type { AstFrameworkPatternConfig } from '../language-provider.js'; import type { SyntaxNode } from '../utils/ast-helpers.js'; import { typeConfig as rubyConfig } from '../type-extractors/ruby.js'; import { routeRubyCall } from '../call-routing.js'; @@ -151,6 +152,31 @@ const rubyResolveEnclosingOwner = (node: SyntaxNode): SyntaxNode | null => { export const rubyProvider = defineLanguage({ id: SupportedLanguages.Ruby, extensions: ['.rb', '.rake', '.gemspec'], + entryPointPatterns: [/^call$/, /^perform$/, /^execute$/], + astFrameworkPatterns: [ + { + framework: 'rails', + entryPointMultiplier: 3.0, + reason: 'rails-pattern', + patterns: [ + 'ApplicationController', + 'ApplicationRecord', + 'ActiveRecord::Base', + 'before_action', + 'after_action', + 'has_many', + 'belongs_to', + 'has_one', + 'validates', + ], + }, + { + framework: 'sinatra', + entryPointMultiplier: 2.8, + reason: 'sinatra-pattern', + patterns: ['Sinatra::Base', 'Sinatra::Application'], + }, + ], treeSitterQueries: RUBY_QUERIES, typeConfig: rubyConfig, exportChecker: rubyExportChecker, diff --git a/gitnexus/src/core/ingestion/languages/rust.ts b/gitnexus/src/core/ingestion/languages/rust.ts index ac0cd5788..e7638e4ea 100644 --- a/gitnexus/src/core/ingestion/languages/rust.ts +++ b/gitnexus/src/core/ingestion/languages/rust.ts @@ -22,6 +22,7 @@ import { createImportResolver } from '../import-resolvers/resolver-factory.js'; import { rustImportConfig } from '../import-resolvers/configs/rust.js'; import { extractRustNamedBindings } from '../named-bindings/rust.js'; import { RUST_QUERIES } from '../tree-sitter-queries.js'; +import type { AstFrameworkPatternConfig } from '../language-provider.js'; import { createFieldExtractor } from '../field-extractors/generic.js'; import { rustConfig as rustFieldConfig } from '../field-extractors/configs/rust.js'; import { createMethodExtractor } from '../method-extractors/generic.js'; @@ -121,6 +122,41 @@ const BUILT_INS: ReadonlySet = new Set([ export const rustProvider = defineLanguage({ id: SupportedLanguages.Rust, extensions: ['.rs'], + entryPointPatterns: [/^(get|post|put|delete)_handler$/i, /^handle_/, /^new$/, /^run$/, /^spawn/], + astFrameworkPatterns: [ + { + framework: 'actix-web', + entryPointMultiplier: 3.0, + reason: 'actix-attribute', + patterns: [ + '#[get', + '#[post', + '#[put', + '#[delete', + '#[actix_web', + 'HttpRequest', + 'HttpResponse', + ], + }, + { + framework: 'axum', + entryPointMultiplier: 3.0, + reason: 'axum-routing', + patterns: ['Router::new', 'axum::extract', 'axum::routing'], + }, + { + framework: 'rocket', + entryPointMultiplier: 3.0, + reason: 'rocket-attribute', + patterns: ['#[get', '#[post', '#[launch', 'rocket::'], + }, + { + framework: 'tokio', + entryPointMultiplier: 2.5, + reason: 'tokio-runtime', + patterns: ['#[tokio::main]', '#[tokio::test]'], + }, + ] satisfies AstFrameworkPatternConfig[], treeSitterQueries: RUST_QUERIES, typeConfig: rustConfig, exportChecker: rustExportChecker, diff --git a/gitnexus/src/core/ingestion/languages/swift.ts b/gitnexus/src/core/ingestion/languages/swift.ts index 7b74386a8..654e7b638 100644 --- a/gitnexus/src/core/ingestion/languages/swift.ts +++ b/gitnexus/src/core/ingestion/languages/swift.ts @@ -15,6 +15,7 @@ import type { NodeLabel, SymbolDefinition } from 'gitnexus-shared'; import { createClassExtractor } from '../class-extractors/generic.js'; import { swiftClassConfig } from '../class-extractors/configs/swift.js'; import { defineLanguage } from '../language-provider.js'; +import type { AstFrameworkPatternConfig } from '../language-provider.js'; import { typeConfig as swiftConfig } from '../type-extractors/swift.js'; import { swiftExportChecker } from '../export-detection.js'; import { createImportResolver } from '../import-resolvers/resolver-factory.js'; @@ -259,6 +260,60 @@ const BUILT_INS: ReadonlySet = new Set([ export const swiftProvider = defineLanguage({ id: SupportedLanguages.Swift, extensions: ['.swift'], + entryPointPatterns: [ + /^viewDidLoad$/, + /^viewWillAppear$/, + /^viewDidAppear$/, + /^viewWillDisappear$/, + /^viewDidDisappear$/, + /^application\(/, + /^scene\(/, + /^body$/, + /Coordinator$/, + /^sceneDidBecomeActive$/, + /^sceneWillResignActive$/, + /^didFinishLaunchingWithOptions$/, + /ViewController$/, + /^configure[A-Z]/, + /^setup[A-Z]/, + /^makeBody$/, + ], + astFrameworkPatterns: [ + { + framework: 'uikit', + entryPointMultiplier: 2.5, + reason: 'uikit-lifecycle', + patterns: [ + 'viewDidLoad', + 'viewWillAppear', + 'viewDidAppear', + 'UIViewController', + '@IBOutlet', + '@IBAction', + '@objc', + ], + }, + { + framework: 'swiftui', + entryPointMultiplier: 2.8, + reason: 'swiftui-pattern', + patterns: [ + '@main', + 'WindowGroup', + 'ContentView', + '@StateObject', + '@ObservedObject', + '@EnvironmentObject', + '@Published', + ], + }, + { + framework: 'vapor', + entryPointMultiplier: 3.0, + reason: 'vapor-routing', + patterns: ['app.get', 'app.post', 'req.content.decode', 'Vapor'], + }, + ], treeSitterQueries: SWIFT_QUERIES, typeConfig: swiftConfig, exportChecker: swiftExportChecker, diff --git a/gitnexus/src/core/ingestion/languages/typescript.ts b/gitnexus/src/core/ingestion/languages/typescript.ts index e2ec9d23f..57751ee41 100644 --- a/gitnexus/src/core/ingestion/languages/typescript.ts +++ b/gitnexus/src/core/ingestion/languages/typescript.ts @@ -10,6 +10,7 @@ import { SupportedLanguages } from 'gitnexus-shared'; import type { NodeLabel } from 'gitnexus-shared'; import { defineLanguage } from '../language-provider.js'; +import type { AstFrameworkPatternConfig } from '../language-provider.js'; import { createClassExtractor } from '../class-extractors/generic.js'; import { typescriptClassConfig, @@ -217,6 +218,29 @@ export const BUILT_INS: ReadonlySet = new Set([ export const typescriptProvider = defineLanguage({ id: SupportedLanguages.TypeScript, extensions: ['.ts', '.tsx'], + entryPointPatterns: [/^use[A-Z]/], + astFrameworkPatterns: [ + { + framework: 'nestjs', + entryPointMultiplier: 3.2, + reason: 'nestjs-decorator', + patterns: ['@Controller', '@Get', '@Post', '@Put', '@Delete', '@Patch'], + }, + { + framework: 'expo-router', + entryPointMultiplier: 2.5, + reason: 'expo-router-navigation', + patterns: [ + 'router.push', + 'router.replace', + 'router.navigate', + 'useRouter', + 'useLocalSearchParams', + 'useSegments', + 'expo-router', + ], + }, + ] satisfies AstFrameworkPatternConfig[], treeSitterQueries: TYPESCRIPT_QUERIES, typeConfig: typescriptConfig, exportChecker: tsExportChecker, @@ -256,6 +280,29 @@ export const typescriptProvider = defineLanguage({ export const javascriptProvider = defineLanguage({ id: SupportedLanguages.JavaScript, extensions: ['.js', '.jsx'], + entryPointPatterns: [/^use[A-Z]/], + astFrameworkPatterns: [ + { + framework: 'nestjs', + entryPointMultiplier: 3.2, + reason: 'nestjs-decorator', + patterns: ['@Controller', '@Get', '@Post', '@Put', '@Delete', '@Patch'], + }, + { + framework: 'expo-router', + entryPointMultiplier: 2.5, + reason: 'expo-router-navigation', + patterns: [ + 'router.push', + 'router.replace', + 'router.navigate', + 'useRouter', + 'useLocalSearchParams', + 'useSegments', + 'expo-router', + ], + }, + ] satisfies AstFrameworkPatternConfig[], treeSitterQueries: JAVASCRIPT_QUERIES, typeConfig: typescriptConfig, exportChecker: tsExportChecker, diff --git a/gitnexus/src/core/ingestion/languages/vue.ts b/gitnexus/src/core/ingestion/languages/vue.ts index 51ab37882..f08fe9050 100644 --- a/gitnexus/src/core/ingestion/languages/vue.ts +++ b/gitnexus/src/core/ingestion/languages/vue.ts @@ -68,6 +68,8 @@ const vueClassExtractor = createClassExtractor(vueClassConfig); export const vueProvider = defineLanguage({ id: SupportedLanguages.Vue, extensions: ['.vue'], + entryPointPatterns: [], + astFrameworkPatterns: [], treeSitterQueries: TYPESCRIPT_QUERIES, typeConfig: typescriptConfig, exportChecker: tsExportChecker, diff --git a/gitnexus/test/unit/framework-detection.test.ts b/gitnexus/test/unit/framework-detection.test.ts index ce9d76f10..4082fd750 100644 --- a/gitnexus/test/unit/framework-detection.test.ts +++ b/gitnexus/test/unit/framework-detection.test.ts @@ -2,8 +2,8 @@ import { describe, it, expect } from 'vitest'; import { detectFrameworkFromPath, detectFrameworkFromAST, - FRAMEWORK_AST_PATTERNS, } from '../../src/core/ingestion/framework-detection.js'; +import { providers } from '../../src/core/ingestion/languages/index.js'; describe('detectFrameworkFromPath', () => { describe('Next.js', () => { @@ -359,24 +359,26 @@ describe('detectFrameworkFromAST', () => { }); }); -describe('FRAMEWORK_AST_PATTERNS', () => { - it('has patterns for all expected frameworks', () => { - const expectedFrameworks = [ +describe('provider astFrameworkPatterns', () => { + it('covers all expected frameworks across providers', () => { + const expectedFrameworks = new Set([ 'nestjs', 'expo-router', - 'express', 'fastapi', 'flask', 'spring', 'jaxrs', 'aspnet', + 'signalr', + 'blazor', + 'efcore', 'go-http', 'gin', 'echo', 'fiber', 'go-grpc', 'laravel', - 'actix', + 'actix-web', 'axum', 'rocket', 'tokio', @@ -386,12 +388,21 @@ describe('FRAMEWORK_AST_PATTERNS', () => { 'vapor', 'rails', 'sinatra', - ]; + 'spring-kotlin', + 'ktor', + 'android-kotlin', + 'flutter', + 'riverpod', + ]); + const foundFrameworks = new Set(); + for (const provider of Object.values(providers)) { + for (const cfg of provider.astFrameworkPatterns ?? []) { + foundFrameworks.add(cfg.framework); + expect(cfg.patterns.length).toBeGreaterThan(0); + } + } for (const fw of expectedFrameworks) { - expect(FRAMEWORK_AST_PATTERNS).toHaveProperty(fw); - expect( - FRAMEWORK_AST_PATTERNS[fw as keyof typeof FRAMEWORK_AST_PATTERNS].length, - ).toBeGreaterThan(0); + expect(foundFrameworks.has(fw), `missing framework: ${fw}`).toBe(true); } }); });