Follow-up to #813: the React resolver's blind components[0] fallback was the demonstrated wrong-edge source, but the Svelte and Vue resolvers had the same flaw in their own shape: - svelte: resolveComponent fell back to components[0] across the whole repo when no same-directory match existed — an arbitrary pick among same-named components in a multi-app monorepo. - vue: resolveComponent returned the FIRST basename-matching .vue file found anywhere in the tree; its same-directory pass below was unreachable dead code. apps/a/Button.vue vs apps/b/Button.vue was a file-enumeration-order coin flip. Both now follow the #764 rule: same-directory first, otherwise only an UNAMBIGUOUS name resolves — ambiguity falls through to the name-matcher's proximity scoring instead of guessing. Safety: zero-delta A/B on the README's own framework benchmark repos (sveltejs/realworld — the 100% Svelte coverage repo — and nuxt/movies, 93.5% Vue coverage) plus the excalidraw control: node counts identical, zero calls or references edges changed. Single-app repos have unique component names, so the rule only bites where the old behavior was already a coin flip. Full suite 1398 passed. Also verified the #813 per-definition tool grouping is language-agnostic (probed Go same-named functions across packages — grouped identically to the TS fixture). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
222af6b87c
commit
763ee9c825
@@ -220,7 +220,11 @@ function resolveComponent(
|
||||
const sameDir = components.filter((n) => n.filePath.startsWith(fromDir));
|
||||
if (sameDir.length > 0) return sameDir[0]!.id;
|
||||
|
||||
return components[0]!.id;
|
||||
// No positional signal: only an UNAMBIGUOUS name may resolve — picking
|
||||
// components[0] chose an arbitrary same-named component in a multi-app
|
||||
// monorepo (#764). Ambiguity falls through to the name-matcher, whose
|
||||
// proximity scoring decides.
|
||||
return components.length === 1 ? components[0]!.id : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -279,39 +279,32 @@ function resolveComponent(
|
||||
fromFile: string,
|
||||
context: ResolutionContext
|
||||
): string | null {
|
||||
const allFiles = context.getAllFiles();
|
||||
const vueFiles = allFiles.filter((f) => f.endsWith('.vue'));
|
||||
|
||||
// Check for exact name match (Button -> Button.vue)
|
||||
for (const file of vueFiles) {
|
||||
// Collect ALL basename matches first. The previous version returned the
|
||||
// FIRST `Button.vue` found anywhere in the tree (its same-directory pass
|
||||
// below was unreachable), so a multi-app monorepo with one `Button.vue`
|
||||
// per app resolved to an arbitrary one (#764).
|
||||
const matches: string[] = [];
|
||||
for (const file of context.getAllFiles()) {
|
||||
if (!file.endsWith('.vue')) continue;
|
||||
const fileName = file.split(/[/\\]/).pop() || '';
|
||||
const componentName = fileName.replace(/\.vue$/, '');
|
||||
if (componentName === name) {
|
||||
const nodes = context.getNodesInFile(file);
|
||||
const component = nodes.find((n) => n.kind === 'component' && n.name === name);
|
||||
if (component) {
|
||||
return component.id;
|
||||
}
|
||||
}
|
||||
if (fileName.replace(/\.vue$/, '') === name) matches.push(file);
|
||||
}
|
||||
if (matches.length === 0) return null;
|
||||
|
||||
// Check same directory first for better specificity
|
||||
const componentIn = (file: string): string | null => {
|
||||
const nodes = context.getNodesInFile(file);
|
||||
const component = nodes.find((n) => n.kind === 'component' && n.name === name);
|
||||
return component ? component.id : null;
|
||||
};
|
||||
|
||||
// Same directory first for specificity
|
||||
const fromDir = fromFile.substring(0, fromFile.lastIndexOf('/'));
|
||||
for (const file of vueFiles) {
|
||||
if (file.startsWith(fromDir)) {
|
||||
const fileName = file.split(/[/\\]/).pop() || '';
|
||||
const componentName = fileName.replace(/\.vue$/, '');
|
||||
if (componentName === name) {
|
||||
const nodes = context.getNodesInFile(file);
|
||||
const component = nodes.find((n) => n.kind === 'component');
|
||||
if (component) {
|
||||
return component.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const sameDir = matches.filter((f) => f.startsWith(fromDir));
|
||||
if (sameDir.length > 0) return componentIn(sameDir[0]!);
|
||||
|
||||
return null;
|
||||
// No positional signal: only an UNAMBIGUOUS basename may resolve;
|
||||
// ambiguity falls through to the name-matcher's proximity scoring.
|
||||
return matches.length === 1 ? componentIn(matches[0]!) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user