diff --git a/__tests__/extraction.test.ts b/__tests__/extraction.test.ts index f4490c237..b66757b9d 100644 --- a/__tests__/extraction.test.ts +++ b/__tests__/extraction.test.ts @@ -43,6 +43,21 @@ describe('Language Detection', () => { expect(detectLanguage('config.mjs')).toBe('javascript'); }); + it('should treat xsjs/xsjslib as plain JavaScript and skip framework extraction', () => { + const code = ` + const app = express(); + app.get('/health', (req, res) => res.send('ok')); + `; + + const jsResult = extractFromSource('app.js', code, 'javascript', ['express']); + const xsjsResult = extractFromSource('app.xsjs', code, 'javascript', ['express']); + const xsjslibResult = extractFromSource('app.xsjslib', code, 'javascript', ['express']); + + expect(jsResult.nodes.some((n) => n.kind === 'route')).toBe(true); + expect(xsjsResult.nodes.some((n) => n.kind === 'route')).toBe(false); + expect(xsjslibResult.nodes.some((n) => n.kind === 'route')).toBe(false); + }); + it('should detect Python files', () => { expect(detectLanguage('main.py')).toBe('python'); }); diff --git a/src/extraction/tree-sitter.ts b/src/extraction/tree-sitter.ts index e6520cc0f..6be16f229 100644 --- a/src/extraction/tree-sitter.ts +++ b/src/extraction/tree-sitter.ts @@ -6525,6 +6525,7 @@ export function extractFromSource( ): ExtractionResult { const detectedLanguage = language || detectLanguage(filePath, source); const fileExtension = path.extname(filePath).toLowerCase(); + const isXsjsLike = fileExtension === '.xsjs' || fileExtension === '.xsjslib'; let result: ExtractionResult; @@ -6582,7 +6583,7 @@ export function extractFromSource( } // Framework-specific extraction (routes, middleware, etc.) - if (frameworkNames && frameworkNames.length > 0) { + if (!isXsjsLike && frameworkNames && frameworkNames.length > 0) { const allResolvers = getAllFrameworkResolvers(); const applicable = getApplicableFrameworks( allResolvers.filter((r) => frameworkNames.includes(r.name)), diff --git a/src/resolution/import-resolver.ts b/src/resolution/import-resolver.ts index bbb1303b3..538b21b7d 100644 --- a/src/resolution/import-resolver.ts +++ b/src/resolution/import-resolver.ts @@ -21,7 +21,7 @@ const EXTENSION_RESOLUTION: Record = { // module-entry convention, hit when a bare workspace import ("data") is // rewritten to the member's directory; lowercase variants for safety. arkts: ['.ets', '.ts', '.d.ts', '.js', '/Index.ets', '/index.ets', '/index.ts', '/index.js'], - javascript: ['.js', '.jsx', '.mjs', '.cjs', '/index.js', '/index.jsx'], + javascript: ['.js', '.jsx', '.mjs', '.cjs', '.xsjs', '.xsjslib', '/index.js', '/index.jsx'], tsx: ['.tsx', '.ts', '.d.ts', '.js', '.jsx', '/index.tsx', '/index.ts', '/index.js'], jsx: ['.jsx', '.js', '/index.jsx', '/index.js'], // SFC consumers import plain TS/JS, sibling components, and barrels diff --git a/src/resolution/index.ts b/src/resolution/index.ts index 1edae5162..51a929fa5 100644 --- a/src/resolution/index.ts +++ b/src/resolution/index.ts @@ -716,6 +716,8 @@ export class ReferenceResolver { * Resolve a single reference */ resolveOne(ref: UnresolvedRef): ResolvedRef | null { + const frameworkDisabledForFile = this.isFrameworkDisabledForFile(ref.filePath); + // Skip built-in/external references if (this.isBuiltInOrExternal(ref)) { return null; @@ -757,7 +759,7 @@ export class ReferenceResolver { !isNixPathImportRef(ref) && !this.hasAnyPossibleMatch(existenceName) && !this.matchesAnyImport(ref) && - !this.frameworks.some((f) => f.claimsReference?.(ref.referenceName)) + (!frameworkDisabledForFile && !this.frameworks.some((f) => f.claimsReference?.(ref.referenceName))) ) { return null; } @@ -806,11 +808,13 @@ export class ReferenceResolver { // JS → native `calls`) — `gateFrameworkLanguage` only drops a type/import // edge between two KNOWN families (see its doc), never a `calls` bridge or // a config↔code edge. - for (const framework of this.frameworks) { - const result = this.gateFrameworkLanguage(framework.resolve(ref, this.context), ref); - if (result) { - if (result.confidence >= 0.9) return result; // High confidence, return immediately - candidates.push(result); + if (!frameworkDisabledForFile) { + for (const framework of this.frameworks) { + const result = this.gateFrameworkLanguage(framework.resolve(ref, this.context), ref); + if (result) { + if (result.confidence >= 0.9) return result; // High confidence, return immediately + candidates.push(result); + } } } @@ -1686,6 +1690,10 @@ export class ReferenceResolver { if (tgt && ref.language && crossesKnownFamily(tgt, ref.language)) return null; return result; } + + private isFrameworkDisabledForFile(filePath: string): boolean { + return /\.(xsjs|xsjslib)$/i.test(filePath); + } } /**