diff --git a/src/services/codefixes/moduleSpecifiers.ts b/src/services/codefixes/moduleSpecifiers.ts index 5279c3cb84..5a08f7d3de 100644 --- a/src/services/codefixes/moduleSpecifiers.ts +++ b/src/services/codefixes/moduleSpecifiers.ts @@ -16,15 +16,18 @@ namespace ts.moduleSpecifiers { const getCanonicalFileName = hostGetCanonicalFileName(host); const sourceDirectory = getDirectoryPath(importingSourceFile.fileName); - return getAllModulePaths(program, moduleSymbol.valueDeclaration.getSourceFile()).map(moduleFileName => { - const global = tryGetModuleNameFromAmbientModule(moduleSymbol) - || tryGetModuleNameFromTypeRoots(compilerOptions, host, getCanonicalFileName, moduleFileName, addJsExtension) - || tryGetModuleNameAsNodeModule(compilerOptions, moduleFileName, host, getCanonicalFileName, sourceDirectory) - || rootDirs && tryGetModuleNameFromRootDirs(rootDirs, moduleFileName, sourceDirectory, getCanonicalFileName); - if (global) { - return [global]; - } + const ambient = tryGetModuleNameFromAmbientModule(moduleSymbol); + if (ambient) return [[ambient]]; + const modulePaths = getAllModulePaths(program, moduleSymbol.valueDeclaration.getSourceFile()); + + const global = mapDefined(modulePaths, moduleFileName => + tryGetModuleNameFromTypeRoots(compilerOptions, host, getCanonicalFileName, moduleFileName, addJsExtension) || + tryGetModuleNameAsNodeModule(compilerOptions, moduleFileName, host, getCanonicalFileName, sourceDirectory) || + rootDirs && tryGetModuleNameFromRootDirs(rootDirs, moduleFileName, sourceDirectory, getCanonicalFileName)); + if (global.length) return global.map(g => [g]); + + return modulePaths.map(moduleFileName => { const relativePath = removeExtensionAndIndexPostFix(ensurePathIsNonModuleName(getRelativePathFromDirectory(sourceDirectory, moduleFileName, getCanonicalFileName)), moduleResolutionKind, addJsExtension); if (!baseUrl || preferences.importModuleSpecifierPreference === "relative") { return [relativePath]; @@ -191,11 +194,12 @@ namespace ts.moduleSpecifiers { // Simplify the full file path to something that can be resolved by Node. // If the module could be imported by a directory name, use that directory's name - let moduleSpecifier = getDirectoryOrExtensionlessFileName(moduleFileName); + const moduleSpecifier = getDirectoryOrExtensionlessFileName(moduleFileName); // Get a path that's relative to node_modules or the importing file's path - moduleSpecifier = getNodeResolvablePath(moduleSpecifier); + // if node_modules folder is in this folder or any of its parent folders, no need to keep it. + if (!startsWith(sourceDirectory, moduleSpecifier.substring(0, parts.topLevelNodeModulesIndex))) return undefined; // If the module was found in @types, get the actual Node package name - return getPackageNameFromAtTypesDirectory(moduleSpecifier); + return getPackageNameFromAtTypesDirectory(moduleSpecifier.substring(parts.topLevelPackageNameIndex + 1)); function getDirectoryOrExtensionlessFileName(path: string): string { // If the file is the main module, it can be imported by the package name @@ -224,17 +228,6 @@ namespace ts.moduleSpecifiers { return fullModulePathWithoutExtension; } - - function getNodeResolvablePath(path: string): string { - const basePath = path.substring(0, parts.topLevelNodeModulesIndex); - if (sourceDirectory.indexOf(basePath) === 0) { - // if node_modules folder is in this folder or any of its parent folders, no need to keep it. - return path.substring(parts.topLevelPackageNameIndex + 1); - } - else { - return ensurePathIsNonModuleName(getRelativePathFromDirectory(sourceDirectory, path, getCanonicalFileName)); - } - } } interface NodeModulePathParts { diff --git a/tests/cases/fourslash/completionsImport_tsx.ts b/tests/cases/fourslash/completionsImport_tsx.ts index ff9d79a3e3..f2f8752082 100644 --- a/tests/cases/fourslash/completionsImport_tsx.ts +++ b/tests/cases/fourslash/completionsImport_tsx.ts @@ -1,7 +1,6 @@ /// // @noLib: true -// @nolib: true // @jsx: preserve // @Filename: /a.tsx diff --git a/tests/cases/fourslash/importNameCodeFix_avoidRelativeNodeModules.ts b/tests/cases/fourslash/importNameCodeFix_avoidRelativeNodeModules.ts new file mode 100644 index 0000000000..bff7fec290 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFix_avoidRelativeNodeModules.ts @@ -0,0 +1,27 @@ +/// + +// @Filename: /a/index.d.ts +// @Symlink: /b/node_modules/a/index.d.ts +// @Symlink: /c/node_modules/a/index.d.ts +////export const a: number; + +// @Filename: /b/index.ts +// @Symlink: /c/node_modules/b/index.d.ts +////import { a } from 'a' +////export const b: number; + +// @Filename: /c/a_user.ts +// Importing from "a" to get /c/node_modules/a in the project. +// Must do this in a separate file to avoid import fixes attempting to share the import. +////import { a } from "a"; + +// @Filename: /c/foo.ts +////[|import { b } from "b"; +////a;|] + +goTo.file("/c/foo.ts"); +verify.importFixAtPosition([ +`import { b } from "b"; +import { a } from "a"; +a;`, +]);