diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts
index 0fa0b72162..4efa601975 100644
--- a/src/services/codefixes/importFixes.ts
+++ b/src/services/codefixes/importFixes.ts
@@ -504,42 +504,60 @@ namespace ts.codefix {
return undefined;
}
- const indexOfNodeModules = moduleFileName.indexOf("node_modules");
- if (indexOfNodeModules < 0) {
+ const indexOfTopNodeModules = moduleFileName.indexOf("node_modules");
+ if (indexOfTopNodeModules < 0) {
return undefined;
}
- let relativeFileName: string;
- if (sourceDirectory.indexOf(moduleFileName.substring(0, indexOfNodeModules - 1)) === 0) {
- // if node_modules folder is in this folder or any of its parent folder, no need to keep it.
- relativeFileName = moduleFileName.substring(indexOfNodeModules + 13 /* "node_modules\".length */);
- }
- else {
- relativeFileName = getRelativePath(moduleFileName, sourceDirectory);
- }
+ // Simplify the full file path to something that can be resolved by Node.
+ // First remove the extension
+ let moduleSpecifier = removeFileExtension(moduleFileName);
+ // If the module could be imported by a directory name, use that directory's name
+ moduleSpecifier = getDirectoryOrFileName(moduleSpecifier);
+ // Get a path that's relative to node_modules or the importing file's path
+ moduleSpecifier = getNodeResolvablePath(moduleSpecifier);
+ // If the module was found in @types, get the actual node package name
+ return getPackageNameFromAtTypesDirectory(moduleSpecifier);
- relativeFileName = removeFileExtension(relativeFileName);
- if (endsWith(relativeFileName, "/index")) {
- relativeFileName = getDirectoryPath(relativeFileName);
- }
- else {
- try {
- const moduleDirectory = getDirectoryPath(moduleFileName);
- const packageJsonContent = JSON.parse(context.host.readFile(combinePaths(moduleDirectory, "package.json")));
+ function getDirectoryOrFileName(fullModulePathWithoutExtension: string): string {
+ // If the file is the main module, it can be imported by the package name
+ const indexOfLastNodeModules = moduleFileName.lastIndexOf("node_modules");
+ const indexOfSlashAtPackageRoot = moduleFileName.indexOf("/", indexOfLastNodeModules + 13 /* "node_modules\".length */);
+ const packageRootPath = moduleFileName.substring(0, indexOfSlashAtPackageRoot);
+ const packageJsonPath = combinePaths(packageRootPath, "package.json");
+ if (context.host.fileExists(packageJsonPath)) {
+ const packageJsonContent = JSON.parse(context.host.readFile(packageJsonPath));
if (packageJsonContent) {
- const mainFile = packageJsonContent.main || packageJsonContent.typings;
- if (mainFile) {
- const mainExportFile = toPath(mainFile, moduleDirectory, getCanonicalFileName);
+ const mainFileRelative = packageJsonContent.typings || packageJsonContent.types || packageJsonContent.main;
+ if (mainFileRelative) {
+ const mainExportFile = toPath(mainFileRelative, packageRootPath, getCanonicalFileName);
if (removeFileExtension(mainExportFile) === removeFileExtension(moduleFileName)) {
- relativeFileName = getDirectoryPath(relativeFileName);
+ return packageRootPath;
}
}
}
}
- catch (e) { }
+
+ // If the file is index.js, it can be imported by its directory name
+ if (endsWith(fullModulePathWithoutExtension, "/index")) {
+ return getDirectoryPath(fullModulePathWithoutExtension);
+ }
+
+ return fullModulePathWithoutExtension;
}
- return getPackageNameFromAtTypesDirectory(relativeFileName);
+ function getNodeResolvablePath(path: string): string {
+ const fullPathUptoNodeModules = moduleFileName.substring(0, indexOfTopNodeModules - 1);
+ if (sourceDirectory.indexOf(fullPathUptoNodeModules) === 0) {
+ const indexOfTopPackageName = indexOfTopNodeModules + 13 /* "node_modules\".length */;
+ // if node_modules folder is in this folder or any of its parent folders, no need to keep it.
+ const relativeToTopNodeModules = path.substring(indexOfTopPackageName);
+ return relativeToTopNodeModules;
+ }
+ else {
+ return getRelativePath(path, sourceDirectory);
+ }
+ }
}
}
diff --git a/tests/cases/fourslash/importNameCodeFixNewImportNodeModules4.ts b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules4.ts
new file mode 100644
index 0000000000..9969bdcf46
--- /dev/null
+++ b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules4.ts
@@ -0,0 +1,25 @@
+///
+
+//// [|f1/*0*/('');|]
+
+// @Filename: package.json
+//// { "dependencies": { "package-name": "latest" } }
+
+// @Filename: node_modules/package-name/bin/lib/libfile.d.ts
+//// export function f1(text: string): string;
+
+// @Filename: node_modules/package-name/bin/lib/libfile.js
+//// function f1(text) { }
+//// exports.f1 = f1;
+
+// @Filename: node_modules/package-name/package.json
+//// {
+//// "main": "bin/lib/libfile.js",
+//// "types": "bin/lib/libfile.d.ts"
+//// }
+
+verify.importFixAtPosition([
+`import { f1 } from "package-name";
+
+f1('');`
+]);
diff --git a/tests/cases/fourslash/importNameCodeFixNewImportNodeModules5.ts b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules5.ts
new file mode 100644
index 0000000000..2410036299
--- /dev/null
+++ b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules5.ts
@@ -0,0 +1,25 @@
+///
+
+//// [|f1/*0*/('');|]
+
+// @Filename: package.json
+//// { "dependencies": { "package-name": "latest" } }
+
+// @Filename: node_modules/package-name/node_modules/package-name2/bin/lib/libfile.d.ts
+//// export function f1(text: string): string;
+
+// @Filename: node_modules/package-name/node_modules/package-name2/bin/lib/libfile.js
+//// function f1(text) { }
+//// exports.f1 = f1;
+
+// @Filename: node_modules/package-name/node_modules/package-name2/package.json
+//// {
+//// "main": "bin/lib/libfile.js",
+//// "types": "bin/lib/libfile.d.ts"
+//// }
+
+verify.importFixAtPosition([
+`import { f1 } from "package-name/node_modules/package-name2";
+
+f1('');`
+]);
diff --git a/tests/cases/fourslash/importNameCodeFixNewImportNodeModules6.ts b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules6.ts
new file mode 100644
index 0000000000..7a52e3f67f
--- /dev/null
+++ b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules6.ts
@@ -0,0 +1,25 @@
+///
+
+//// [|f1/*0*/('');|]
+
+// @Filename: package.json
+//// { "dependencies": { "package-name": "latest" } }
+
+// @Filename: node_modules/package-name/bin/lib/index.d.ts
+//// export function f1(text: string): string;
+
+// @Filename: node_modules/package-name/bin/lib/index.js
+//// function f1(text) { }
+//// exports.f1 = f1;
+
+// @Filename: node_modules/package-name/package.json
+//// {
+//// "main": "bin/lib/index.js",
+//// "types": "bin/lib/index.d.ts"
+//// }
+
+verify.importFixAtPosition([
+`import { f1 } from "package-name";
+
+f1('');`
+]);