diff --git a/src/compiler/core.ts b/src/compiler/core.ts index f3dd99ee5bfde..0d6be577d5054 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2169,6 +2169,10 @@ namespace ts { return expectedPos >= 0 && str.indexOf(suffix, expectedPos) === expectedPos; } + export function removeSuffix(str: string, suffix: string): string { + return endsWith(str, suffix) ? str.slice(0, str.length - suffix.length) : str; + } + export function stringContains(str: string, substring: string): boolean { return str.indexOf(substring) !== -1; } diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 2687a660ca8e4..fb801e474613c 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2549,6 +2549,9 @@ Actual: ${stringify(fullActual)}`); } const sortedExpectedArray = expectedTextArray.sort(); const sortedActualArray = actualTextArray.sort(); + if (sortedExpectedArray.length !== sortedActualArray.length) { + this.raiseError(`Expected ${sortedExpectedArray.length} import fixes, got ${sortedActualArray.length}`); + } ts.zipWith(sortedExpectedArray, sortedActualArray, (expected, actual, index) => { if (expected !== actual) { this.raiseError(`Import fix at index ${index} doesn't match.\n${showTextDiff(expected, actual)}`); @@ -2867,7 +2870,7 @@ Actual: ${stringify(fullActual)}`); if (negative) { if (codeFixes.length) { - this.raiseError(`verifyCodeFixAvailable failed - expected no fixes but found one.`); + this.raiseError(`verifyCodeFixAvailable failed - expected no fixes but found ${codeFixes.map(c => c.description)}.`); } return; } diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 838e19fa35de1..ed6a3d485a4e2 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -322,7 +322,7 @@ namespace ts.codefix { tryGetModuleNameAsNodeModule(options, moduleFileName, host, getCanonicalFileName, sourceDirectory) || tryGetModuleNameFromBaseUrl(options, moduleFileName, getCanonicalFileName) || options.rootDirs && tryGetModuleNameFromRootDirs(options.rootDirs, moduleFileName, sourceDirectory, getCanonicalFileName) || - removeFileExtension(getRelativePath(moduleFileName, sourceDirectory, getCanonicalFileName)); + removeExtensionAndIndexPostFix(getRelativePath(moduleFileName, sourceDirectory, getCanonicalFileName), options); } function tryGetModuleNameFromAmbientModule(moduleSymbol: Symbol): string | undefined { @@ -343,7 +343,7 @@ namespace ts.codefix { } const relativeNameWithIndex = removeFileExtension(relativeName); - relativeName = removeExtensionAndIndexPostFix(relativeName); + relativeName = removeExtensionAndIndexPostFix(relativeName, options); if (options.paths) { for (const key in options.paths) { @@ -393,7 +393,7 @@ namespace ts.codefix { return roots && firstDefined(roots, unNormalizedTypeRoot => { const typeRoot = toPath(unNormalizedTypeRoot, /*basePath*/ undefined, getCanonicalFileName); if (startsWith(moduleFileName, typeRoot)) { - return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1)); + return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), options); } }); } @@ -527,12 +527,9 @@ namespace ts.codefix { return firstDefined(rootDirs, rootDir => getRelativePathIfInDirectory(path, rootDir, getCanonicalFileName)); } - function removeExtensionAndIndexPostFix(fileName: string) { - fileName = removeFileExtension(fileName); - if (endsWith(fileName, "/index")) { - fileName = fileName.substr(0, fileName.length - 6/* "/index".length */); - } - return fileName; + function removeExtensionAndIndexPostFix(fileName: string, options: CompilerOptions): string { + const noExtension = removeFileExtension(fileName); + return getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeJs ? removeSuffix(noExtension, "/index") : noExtension; } function getRelativePathIfInDirectory(path: string, directoryPath: string, getCanonicalFileName: (fileName: string) => string): string | undefined { diff --git a/tests/cases/fourslash/importNameCodeFixNewImportIndex.ts b/tests/cases/fourslash/importNameCodeFixNewImportIndex.ts new file mode 100644 index 0000000000000..2840424c468a7 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportIndex.ts @@ -0,0 +1,16 @@ +/// + +// @Filename: /a/index.ts +////export const foo = 0; + +// @Filename: /b.ts +////[|/**/foo;|] + +goTo.file("/a/index.ts"); +goTo.file("/b.ts"); + +verify.importFixAtPosition([ +`import { foo } from "./a"; + +foo;` +]); diff --git a/tests/cases/fourslash/importNameCodeFixNewImportIndex_notForClassicResolution.ts b/tests/cases/fourslash/importNameCodeFixNewImportIndex_notForClassicResolution.ts new file mode 100644 index 0000000000000..3fa9b91b264a4 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportIndex_notForClassicResolution.ts @@ -0,0 +1,27 @@ +/// + +// @moduleResolution: classic + +// @Filename: /a/index.ts +////export const foo = 0; + +// @Filename: /node_modules/x/index.d.ts +////export const bar = 0; + +// @Filename: /b.ts +////[|foo;|] + +// @Filename: /c.ts +////bar; + +goTo.file("/a/index.ts"); + +goTo.file("/b.ts"); +verify.importFixAtPosition([ +`import { foo } from "./a/index"; + +foo;` +]); + +goTo.file("/c.ts"); +// TODO: GH#20050 verify.not.codeFixAvailable(); diff --git a/tests/cases/fourslash/importNameCodeFixUMDGlobalReact0.ts b/tests/cases/fourslash/importNameCodeFixUMDGlobalReact0.ts index 5b6df7a517349..cd3d25030fa5e 100644 --- a/tests/cases/fourslash/importNameCodeFixUMDGlobalReact0.ts +++ b/tests/cases/fourslash/importNameCodeFixUMDGlobalReact0.ts @@ -3,6 +3,7 @@ // @jsx: react // @allowSyntheticDefaultImports: false // @module: es2015 +// @moduleResolution: node // @Filename: /node_modules/@types/react/index.d.ts ////export = React; diff --git a/tests/cases/fourslash/importNameCodeFixUMDGlobalReact1.ts b/tests/cases/fourslash/importNameCodeFixUMDGlobalReact1.ts index 669db55342b04..9dba0f3988256 100644 --- a/tests/cases/fourslash/importNameCodeFixUMDGlobalReact1.ts +++ b/tests/cases/fourslash/importNameCodeFixUMDGlobalReact1.ts @@ -3,6 +3,7 @@ // @jsx: react // @allowSyntheticDefaultImports: false // @module: es2015 +// @moduleResolution: node // @Filename: /node_modules/@types/react/index.d.ts ////export = React;