From 8e42d2dfb4a400c3e7f0bd7513c3eed109ad731b Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Wed, 24 Jun 2026 07:18:14 +0000 Subject: [PATCH] fix: address Nuxt auto-import review follow-ups --- .../languages/typescript/nuxt-auto-imports.ts | 8 +++++-- .../languages/typescript/scope-resolver.ts | 22 +++++++++++++++-- .../nuxt-auto-imports/pages/type-only.ts | 7 ++++++ .../integration/resolvers/nuxt-scope.test.ts | 24 +++++++++++++------ .../typescript/nuxt-auto-imports.test.ts | 5 ++++ 5 files changed, 55 insertions(+), 11 deletions(-) create mode 100644 gitnexus/test/fixtures/lang-resolution/nuxt-auto-imports/pages/type-only.ts 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 adb9a6ce0..608082136 100644 --- a/gitnexus/src/core/ingestion/languages/typescript/nuxt-auto-imports.ts +++ b/gitnexus/src/core/ingestion/languages/typescript/nuxt-auto-imports.ts @@ -271,7 +271,7 @@ async function dirExists(dirPath: string): Promise { } } -/** Recursively collect `.ts` and `.tsx` files under a directory. */ +/** Recursively collect supported TypeScript/JavaScript files under a directory. */ async function collectTsFiles(dir: string): Promise { const results: string[] = []; let entries: import('fs').Dirent[]; @@ -285,13 +285,17 @@ async function collectTsFiles(dir: string): Promise { const full = path.join(dir, entry.name); if (entry.isDirectory()) { results.push(...(await collectTsFiles(full))); - } else if (entry.isFile() && (entry.name.endsWith('.ts') || entry.name.endsWith('.tsx'))) { + } else if (entry.isFile() && hasSupportedSourceExtension(entry.name)) { results.push(full); } } return results; } +function hasSupportedSourceExtension(fileName: string): boolean { + return FILE_EXTENSIONS.some((ext) => fileName.endsWith(ext)); +} + function extractNitroExportNames(content: string): string[] { const names = new Set(); diff --git a/gitnexus/src/core/ingestion/languages/typescript/scope-resolver.ts b/gitnexus/src/core/ingestion/languages/typescript/scope-resolver.ts index 2e40e85fb..6c77e4c29 100644 --- a/gitnexus/src/core/ingestion/languages/typescript/scope-resolver.ts +++ b/gitnexus/src/core/ingestion/languages/typescript/scope-resolver.ts @@ -12,7 +12,7 @@ * ./query.ts (TYPESCRIPT_SCOPE_QUERY constant). */ -import type { ParsedFile, ScopeId } from 'gitnexus-shared'; +import type { NodeLabel, ParsedFile, ScopeId } from 'gitnexus-shared'; import { SupportedLanguages } from 'gitnexus-shared'; import { generateId } from '../../../../lib/utils.js'; import { buildMro, defaultLinearize } from '../../scope-resolution/passes/mro.js'; @@ -43,6 +43,16 @@ interface TypescriptResolutionConfig { readonly nuxtAutoImports: NuxtAutoImportConfig | null; } +const TYPESCRIPT_TYPE_ONLY_BINDING_TYPES = new Set([ + 'Interface', + 'Type', + 'TypeAlias', + 'Typedef', + 'Trait', + 'Annotation', + 'Decorator', +]); + /** * Build a `resolveImportTarget` adapter that memoizes the workspace * file list, the lower-cased file list, and the per-pass `resolveCache` @@ -278,6 +288,9 @@ const typescriptScopeResolver: ScopeResolver = { }, }; +function occupiesTypeScriptValueSpace(type: NodeLabel): boolean { + return !TYPESCRIPT_TYPE_ONLY_BINDING_TYPES.has(type); +} function hasLocalBindingInScopeChain( scopeId: ScopeId, @@ -295,7 +308,12 @@ function hasLocalBindingInScopeChain( const localBindings = scope.bindings.get(name); if ( - localBindings?.some((binding) => binding.origin === 'local' && binding.def.filePath === filePath) + localBindings?.some( + (binding) => + binding.origin === 'local' && + binding.def.filePath === filePath && + occupiesTypeScriptValueSpace(binding.def.type), + ) ) { return true; } diff --git a/gitnexus/test/fixtures/lang-resolution/nuxt-auto-imports/pages/type-only.ts b/gitnexus/test/fixtures/lang-resolution/nuxt-auto-imports/pages/type-only.ts new file mode 100644 index 000000000..4d3f40028 --- /dev/null +++ b/gitnexus/test/fixtures/lang-resolution/nuxt-auto-imports/pages/type-only.ts @@ -0,0 +1,7 @@ +interface useAuto { + value: string; +} + +export function renderTypeOnly() { + return useAuto(); +} diff --git a/gitnexus/test/integration/resolvers/nuxt-scope.test.ts b/gitnexus/test/integration/resolvers/nuxt-scope.test.ts index b3e3f85a7..16d9fc69e 100644 --- a/gitnexus/test/integration/resolvers/nuxt-scope.test.ts +++ b/gitnexus/test/integration/resolvers/nuxt-scope.test.ts @@ -1,11 +1,6 @@ import { describe, it, expect, beforeAll } from 'vitest'; import path from 'path'; -import { - FIXTURES, - getRelationships, - runPipelineFromRepo, - type PipelineResult, -} from './helpers.js'; +import { FIXTURES, getRelationships, runPipelineFromRepo, type PipelineResult } from './helpers.js'; describe('Nuxt/Nitro auto-import scope resolution', () => { let result: PipelineResult; @@ -17,7 +12,9 @@ describe('Nuxt/Nitro auto-import scope resolution', () => { }, 60000); function nuxtCalls() { - return getRelationships(result, 'CALLS').filter((edge) => edge.rel.reason === 'nuxt-auto-import'); + return getRelationships(result, 'CALLS').filter( + (edge) => edge.rel.reason === 'nuxt-auto-import', + ); } it('prefers server/utils over client composables for same-named server calls', () => { @@ -88,6 +85,19 @@ describe('Nuxt/Nitro auto-import scope resolution', () => { expect(calls.filter((edge) => edge.sourceFilePath.endsWith('pages/noise.ts'))).toHaveLength(0); }); + it('allows type-only local declarations to coexist with value auto-import calls', () => { + const calls = nuxtCalls(); + + expect( + calls.find( + (edge) => + edge.sourceFilePath.endsWith('pages/type-only.ts') && + edge.target === 'useAuto' && + edge.targetFilePath.endsWith('composables/useAuto.ts'), + ), + ).toBeDefined(); + }); + it('suppresses only explicitly imported local names, not every symbol from the same source', () => { 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 4db56eb02..31149c5ad 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 @@ -98,6 +98,7 @@ describe('loadNuxtAutoImports', () => { 'export var delta = () => {};', ].join('\n'), ); + writeFile(root, 'server/utils/jsHelper.js', 'export function jsServerTool() {}'); writeFile(root, 'server/utils/dist/generated.ts', 'export function generatedUtility() {}'); const config = await loadNuxtAutoImports(root); @@ -111,7 +112,11 @@ describe('loadNuxtAutoImports', () => { 'delta', 'fn', 'gamma', + 'jsServerTool', ]); + expect(config!.serverByLocalName.get('jsServerTool')).toMatchObject({ + sourceFile: 'server/utils/jsHelper.js', + }); }); });