Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions __tests__/extraction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
Expand Down
3 changes: 2 additions & 1 deletion src/extraction/tree-sitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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)),
Expand Down
2 changes: 1 addition & 1 deletion src/resolution/import-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const EXTENSION_RESOLUTION: Record<string, string[]> = {
// 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
Expand Down
20 changes: 14 additions & 6 deletions src/resolution/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
}
}
}

Expand Down Expand Up @@ -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);
}
}

/**
Expand Down