From 6fbe4abb730bc26b3605b6cc0137315d68d90074 Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Wed, 24 Jun 2026 08:40:09 +0000 Subject: [PATCH] fix(ingestion): stop Nitro server callers resolving client composables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `getNuxtAutoImportEntry` fell back to the client composable map when a `server/api|routes|middleware` caller's name had no `server/utils` entry. But Nitro only auto-imports `server/utils/**` into the server context — app `composables/` are Vue-app-only — so that fallback minted CALLS/IMPORTS edges Nitro never creates (e.g. a server route "calling" a composable it cannot see without an explicit import). Server callers now resolve the server map only. Restructure the barrel-directory integration test to use a client caller (which legitimately auto-imports the composable) so `index.*` resolution stays covered, and add a negative assertion that `server/api/route.ts` emits no edge to `composables/*` while its real `server/utils` call still resolves. Unit test locks that a server caller does not fall back to a client-only name. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01T4W25WLfYD1JNy8icxeLPU --- .../languages/typescript/nuxt-auto-imports.ts | 6 ++++- .../lang-resolution/nuxt-auto-imports/app.ts | 1 + .../integration/resolvers/nuxt-scope.test.ts | 27 +++++++++++++++++-- .../typescript/nuxt-auto-imports.test.ts | 2 ++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/gitnexus/src/core/ingestion/languages/typescript/nuxt-auto-imports.ts b/gitnexus/src/core/ingestion/languages/typescript/nuxt-auto-imports.ts index 205d66383..905ddfe63 100644 --- a/gitnexus/src/core/ingestion/languages/typescript/nuxt-auto-imports.ts +++ b/gitnexus/src/core/ingestion/languages/typescript/nuxt-auto-imports.ts @@ -123,7 +123,11 @@ export function getNuxtAutoImportEntry( callerFile: string, ): NuxtAutoImportEntry | undefined { if (isNitroServerRuntimeFile(callerFile)) { - return config.serverByLocalName.get(localName) ?? config.clientByLocalName.get(localName); + // Nitro auto-imports only `server/utils/**` into the server context; app + // `composables/` are Vue-app-only. Server callers therefore resolve the + // server map only — no client fallback, which would emit cross-context + // CALLS edges Nitro never actually creates. + return config.serverByLocalName.get(localName); } return config.clientByLocalName.get(localName); } diff --git a/gitnexus/test/fixtures/lang-resolution/nuxt-auto-imports/app.ts b/gitnexus/test/fixtures/lang-resolution/nuxt-auto-imports/app.ts index 4285d154b..b7082c582 100644 --- a/gitnexus/test/fixtures/lang-resolution/nuxt-auto-imports/app.ts +++ b/gitnexus/test/fixtures/lang-resolution/nuxt-auto-imports/app.ts @@ -1,5 +1,6 @@ export function renderClient() { validate(); useAuto(); + useBarrel(); serverOnly(); } diff --git a/gitnexus/test/integration/resolvers/nuxt-scope.test.ts b/gitnexus/test/integration/resolvers/nuxt-scope.test.ts index 16d9fc69e..9dc453e8a 100644 --- a/gitnexus/test/integration/resolvers/nuxt-scope.test.ts +++ b/gitnexus/test/integration/resolvers/nuxt-scope.test.ts @@ -64,7 +64,7 @@ describe('Nuxt/Nitro auto-import scope resolution', () => { expect( calls.find( (edge) => - edge.sourceFilePath.endsWith('server/api/route.ts') && + edge.sourceFilePath.endsWith('app.ts') && edge.target === 'useBarrel' && edge.targetFilePath.endsWith('composables/group/index.ts'), ), @@ -72,12 +72,35 @@ describe('Nuxt/Nitro auto-import scope resolution', () => { expect( imports.find( (edge) => - edge.sourceFilePath.endsWith('server/api/route.ts') && + edge.sourceFilePath.endsWith('app.ts') && edge.targetFilePath.endsWith('composables/group/index.ts'), ), ).toBeDefined(); }); + it('does not resolve client composables from Nitro server callers (no client fallback)', () => { + const calls = nuxtCalls(); + // server/api/route.ts calls validate() (a real server/util), useAuto() and + // useBarrel() (client-only composables). Only the server/util resolves; + // Nitro does not auto-import composables/ server-side, so no edge is emitted + // to either composable. + const composableEdges = calls.filter( + (edge) => + edge.sourceFilePath.endsWith('server/api/route.ts') && + edge.targetFilePath.includes('/composables/'), + ); + expect(composableEdges).toHaveLength(0); + // The legitimate server/util edge still resolves. + expect( + calls.find( + (edge) => + edge.sourceFilePath.endsWith('server/api/route.ts') && + edge.target === 'validate' && + edge.targetFilePath.endsWith('server/utils/serverValidate.ts'), + ), + ).toBeDefined(); + }); + it('does not emit auto-import edges for local shadowing or lexical noise', () => { const calls = nuxtCalls(); diff --git a/gitnexus/test/unit/scope-resolution/typescript/nuxt-auto-imports.test.ts b/gitnexus/test/unit/scope-resolution/typescript/nuxt-auto-imports.test.ts index 19ac329be..7fd27b4db 100644 --- a/gitnexus/test/unit/scope-resolution/typescript/nuxt-auto-imports.test.ts +++ b/gitnexus/test/unit/scope-resolution/typescript/nuxt-auto-imports.test.ts @@ -68,6 +68,8 @@ describe('loadNuxtAutoImports', () => { expect(getNuxtAutoImportEntry(config!, 'validate', 'app.vue')).toMatchObject({ sourceFile: 'composables/clientValidate.ts', }); + // A Nitro server caller does NOT fall back to a client-only composable. + expect(getNuxtAutoImportEntry(config!, 'useAlias', 'server/api/users.ts')).toBeUndefined(); }); it('resolves extensionless directory imports to index files', async () => {