Skip to content

Commit 7588c15

Browse files
author
Andy Hanson
committed
In import fixes, use a ".js" extension if other imports do
1 parent 0477f91 commit 7588c15

File tree

3 files changed

+37
-7
lines changed

3 files changed

+37
-7
lines changed

src/services/codefixes/importFixes.ts

+16-6
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,10 @@ namespace ts.codefix {
306306
return literal;
307307
}
308308

309+
function usesJsExtensionOnImports(sourceFile: SourceFile): boolean {
310+
return firstDefined(sourceFile.imports, ({ text }) => pathIsRelative(text) ? fileExtensionIs(text, Extension.Js) : undefined) || false;
311+
}
312+
309313
function createImportClauseOfKind(kind: ImportKind.Default | ImportKind.Named | ImportKind.Namespace, symbolName: string) {
310314
const id = createIdentifier(symbolName);
311315
switch (kind) {
@@ -329,18 +333,19 @@ namespace ts.codefix {
329333
host: LanguageServiceHost,
330334
): string[] {
331335
const { baseUrl, paths, rootDirs } = options;
336+
const addJsExtension = usesJsExtensionOnImports(sourceFile);
332337
const choicesForEachExportingModule = flatMap(moduleSymbols, moduleSymbol =>
333338
getAllModulePaths(program, moduleSymbol.valueDeclaration.getSourceFile()).map(moduleFileName => {
334339
const sourceDirectory = getDirectoryPath(sourceFile.fileName);
335340
const global = tryGetModuleNameFromAmbientModule(moduleSymbol)
336-
|| tryGetModuleNameFromTypeRoots(options, host, getCanonicalFileName, moduleFileName)
341+
|| tryGetModuleNameFromTypeRoots(options, host, getCanonicalFileName, moduleFileName, addJsExtension)
337342
|| tryGetModuleNameAsNodeModule(options, moduleFileName, host, getCanonicalFileName, sourceDirectory)
338343
|| rootDirs && tryGetModuleNameFromRootDirs(rootDirs, moduleFileName, sourceDirectory, getCanonicalFileName);
339344
if (global) {
340345
return [global];
341346
}
342347

343-
const relativePath = removeExtensionAndIndexPostFix(getRelativePath(moduleFileName, sourceDirectory, getCanonicalFileName), options);
348+
const relativePath = removeExtensionAndIndexPostFix(getRelativePath(moduleFileName, sourceDirectory, getCanonicalFileName), options, addJsExtension);
344349
if (!baseUrl) {
345350
return [relativePath];
346351
}
@@ -350,7 +355,7 @@ namespace ts.codefix {
350355
return [relativePath];
351356
}
352357

353-
const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, options);
358+
const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, options, addJsExtension);
354359
if (paths) {
355360
const fromPaths = tryGetModuleNameFromPaths(removeFileExtension(relativeToBaseUrl), importRelativeToBaseUrl, paths);
356361
if (fromPaths) {
@@ -459,12 +464,13 @@ namespace ts.codefix {
459464
host: GetEffectiveTypeRootsHost,
460465
getCanonicalFileName: (file: string) => string,
461466
moduleFileName: string,
467+
addJsExtension: boolean,
462468
): string | undefined {
463469
const roots = getEffectiveTypeRoots(options, host);
464470
return roots && firstDefined(roots, unNormalizedTypeRoot => {
465471
const typeRoot = toPath(unNormalizedTypeRoot, /*basePath*/ undefined, getCanonicalFileName);
466472
if (startsWith(moduleFileName, typeRoot)) {
467-
return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), options);
473+
return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), options, addJsExtension);
468474
}
469475
});
470476
}
@@ -598,9 +604,13 @@ namespace ts.codefix {
598604
return firstDefined(rootDirs, rootDir => getRelativePathIfInDirectory(path, rootDir, getCanonicalFileName));
599605
}
600606

601-
function removeExtensionAndIndexPostFix(fileName: string, options: CompilerOptions): string {
607+
function removeExtensionAndIndexPostFix(fileName: string, options: CompilerOptions, addJsExtension: boolean): string {
602608
const noExtension = removeFileExtension(fileName);
603-
return getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeJs ? removeSuffix(noExtension, "/index") : noExtension;
609+
return addJsExtension
610+
? noExtension + ".js"
611+
: getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeJs
612+
? removeSuffix(noExtension, "/index")
613+
: noExtension;
604614
}
605615

606616
function getRelativePathIfInDirectory(path: string, directoryPath: string, getCanonicalFileName: GetCanonicalFileName): string | undefined {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @moduleResolution: node
4+
// @noLib: true
5+
6+
// @Filename: /a.ts
7+
////export function a() {}
8+
9+
// @Filename: /b.ts
10+
////export function b() {}
11+
12+
// @Filename: /c.ts
13+
////import { a } from "./a.js";
14+
////[|b;|]
15+
16+
goTo.file("/c.ts");
17+
verify.importFixAtPosition([
18+
`import { b } from "./b.js";
19+
b;`,
20+
]);

tests/cases/fourslash/importNameCodeFix_symlink.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
////import { foo } from "link";
1212

1313
// @Filename: /b.ts
14-
////[|foo/**/;|]
14+
////[|foo;|]
1515

1616
// Uses "link" instead of "real" because `a` did.
1717
goTo.file("/b.ts");

0 commit comments

Comments
 (0)