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
17 changes: 17 additions & 0 deletions src/services/codefixes/importFixes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1414,6 +1414,18 @@
}

/** @returns `Comparison.LessThan` if `a` is better than `b`. */
/** Heuristic approach: Prioritize local/relative imports over node_modules imports. */

Check failure on line 1417 in src/services/codefixes/importFixes.ts

View workflow job for this annotation

GitHub Actions / lint

Declaration has multiple JSDoc comments
function compareLocalVsExternal(
a: ImportFixWithModuleSpecifier,
b: ImportFixWithModuleSpecifier
): Comparison {
const aIsExternal = a.moduleSpecifierKind === "node_modules";
const bIsExternal = b.moduleSpecifierKind === "node_modules";
if (!aIsExternal && bIsExternal) return Comparison.LessThan;
if (aIsExternal && !bIsExternal) return Comparison.GreaterThan;
return Comparison.EqualTo;
}

function compareModuleSpecifiers(
a: ImportFixWithModuleSpecifier,
b: ImportFixWithModuleSpecifier,
Expand All @@ -1424,6 +1436,11 @@
toPath: (fileName: string) => Path,
): Comparison {
if (a.kind !== ImportFixKind.UseNamespace && b.kind !== ImportFixKind.UseNamespace) {

const localVsExternalComparison = compareLocalVsExternal(a, b);
if (localVsExternalComparison !== Comparison.EqualTo) {
return localVsExternalComparison;
}
return compareBooleans(
b.moduleSpecifierKind !== "node_modules" || allowsImportingSpecifier(b.moduleSpecifier),
a.moduleSpecifierKind !== "node_modules" || allowsImportingSpecifier(a.moduleSpecifier),
Expand Down
54 changes: 54 additions & 0 deletions tests/cases/fourslash/importFixesPrioritizeLocal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/// <reference path="fourslash.ts" />

// Test that local imports are prioritized over external node_modules imports
// when the same symbol is exported from both

// @Filename: /node_modules/@mui/material/index.d.ts
//// export function useTheme(): { palette: string };

// @Filename: /node_modules/@mui/material/package.json
//// { "name": "@mui/material", "version": "5.0.0", "types": "index.d.ts" }

// @Filename: /node_modules/zustand/index.d.ts
//// export function useStore(): any;

// @Filename: /node_modules/zustand/package.json
//// { "name": "zustand", "version": "4.0.0", "types": "index.d.ts" }

// @Filename: /utils/store.ts
//// export function useTheme() {
//// return { palette: 'light' };
//// }
//// export function useStore() {
//// return {};
//// }

// @Filename: /components/Button.tsx
//// // Local useTheme should be suggested first
//// const theme = useTheme/*1*/();

// @Filename: /components/Header.tsx
//// // Local useStore should be suggested first
//// const store = useStore/*2*/();

// Test 1: useTheme - should prioritize local import over @mui/material
goTo.marker("1");
verify.importFixAtPosition([
`import { useTheme } from "../utils/store";
// Local useTheme should be suggested first
const theme = useTheme();`,
`import { useTheme } from "@mui/material";
// Local useTheme should be suggested first
const theme = useTheme();`
]);

// Test 2: useStore - should prioritize local import over zustand
goTo.marker("2");
verify.importFixAtPosition([
`import { useStore } from "../utils/store";
// Local useStore should be suggested first
const store = useStore();`,
`import { useStore } from "zustand";
// Local useStore should be suggested first
const store = useStore();`
]);
Loading