Skip to content

Commit fffc5f9

Browse files
author
Andy Hanson
committed
In import fixes, use a ".js" extension if other imports do
1 parent 484758a commit fffc5f9

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
@@ -302,6 +302,10 @@ namespace ts.codefix {
302302
return literal;
303303
}
304304

305+
function usesJsExtensionOnImports(sourceFile: SourceFile): boolean {
306+
return firstDefined(sourceFile.imports, ({ text }) => pathIsRelative(text) ? fileExtensionIs(text, Extension.Js) : undefined) || false;
307+
}
308+
305309
function createImportClauseOfKind(kind: ImportKind.Default | ImportKind.Named | ImportKind.Namespace, symbolName: string) {
306310
const id = createIdentifier(symbolName);
307311
switch (kind) {
@@ -325,18 +329,19 @@ namespace ts.codefix {
325329
host: LanguageServiceHost,
326330
): string[] {
327331
const { baseUrl, paths, rootDirs } = options;
332+
const addJsExtension = usesJsExtensionOnImports(sourceFile);
328333
const choicesForEachExportingModule = flatMap(moduleSymbols, moduleSymbol =>
329334
getAllModulePaths(program, moduleSymbol.valueDeclaration.getSourceFile()).map(moduleFileName => {
330335
const sourceDirectory = getDirectoryPath(sourceFile.fileName);
331336
const global = tryGetModuleNameFromAmbientModule(moduleSymbol)
332-
|| tryGetModuleNameFromTypeRoots(options, host, getCanonicalFileName, moduleFileName)
337+
|| tryGetModuleNameFromTypeRoots(options, host, getCanonicalFileName, moduleFileName, addJsExtension)
333338
|| tryGetModuleNameAsNodeModule(options, moduleFileName, host, getCanonicalFileName, sourceDirectory)
334339
|| rootDirs && tryGetModuleNameFromRootDirs(rootDirs, moduleFileName, sourceDirectory, getCanonicalFileName);
335340
if (global) {
336341
return [global];
337342
}
338343

339-
const relativePath = removeExtensionAndIndexPostFix(getRelativePath(moduleFileName, sourceDirectory, getCanonicalFileName), options);
344+
const relativePath = removeExtensionAndIndexPostFix(getRelativePath(moduleFileName, sourceDirectory, getCanonicalFileName), options, addJsExtension);
340345
if (!baseUrl) {
341346
return [relativePath];
342347
}
@@ -346,7 +351,7 @@ namespace ts.codefix {
346351
return [relativePath];
347352
}
348353

349-
const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, options);
354+
const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, options, addJsExtension);
350355
if (paths) {
351356
const fromPaths = tryGetModuleNameFromPaths(removeFileExtension(relativeToBaseUrl), importRelativeToBaseUrl, paths);
352357
if (fromPaths) {
@@ -455,12 +460,13 @@ namespace ts.codefix {
455460
host: GetEffectiveTypeRootsHost,
456461
getCanonicalFileName: (file: string) => string,
457462
moduleFileName: string,
463+
addJsExtension: boolean,
458464
): string | undefined {
459465
const roots = getEffectiveTypeRoots(options, host);
460466
return roots && firstDefined(roots, unNormalizedTypeRoot => {
461467
const typeRoot = toPath(unNormalizedTypeRoot, /*basePath*/ undefined, getCanonicalFileName);
462468
if (startsWith(moduleFileName, typeRoot)) {
463-
return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), options);
469+
return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), options, addJsExtension);
464470
}
465471
});
466472
}
@@ -594,9 +600,13 @@ namespace ts.codefix {
594600
return firstDefined(rootDirs, rootDir => getRelativePathIfInDirectory(path, rootDir, getCanonicalFileName));
595601
}
596602

597-
function removeExtensionAndIndexPostFix(fileName: string, options: CompilerOptions): string {
603+
function removeExtensionAndIndexPostFix(fileName: string, options: CompilerOptions, addJsExtension: boolean): string {
598604
const noExtension = removeFileExtension(fileName);
599-
return getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeJs ? removeSuffix(noExtension, "/index") : noExtension;
605+
return addJsExtension
606+
? noExtension + ".js"
607+
: getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeJs
608+
? removeSuffix(noExtension, "/index")
609+
: noExtension;
600610
}
601611

602612
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)