In import fixes, use a ".js" extension if other imports do (#20624)

* In import fixes, use a ".js" extension if other imports do

* Code review
This commit is contained in:
Andy 2018-01-03 08:20:53 -08:00 committed by GitHub
parent 29378b2ce2
commit 74b8160430
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 39 additions and 7 deletions

View File

@ -306,6 +306,10 @@ namespace ts.codefix {
return literal;
}
function usesJsExtensionOnImports(sourceFile: SourceFile): boolean {
return firstDefined(sourceFile.imports, ({ text }) => pathIsRelative(text) ? fileExtensionIs(text, Extension.Js) : undefined) || false;
}
function createImportClauseOfKind(kind: ImportKind.Default | ImportKind.Named | ImportKind.Namespace, symbolName: string) {
const id = createIdentifier(symbolName);
switch (kind) {
@ -329,18 +333,19 @@ namespace ts.codefix {
host: LanguageServiceHost,
): string[] {
const { baseUrl, paths, rootDirs } = options;
const addJsExtension = usesJsExtensionOnImports(sourceFile);
const choicesForEachExportingModule = flatMap(moduleSymbols, moduleSymbol =>
getAllModulePaths(program, moduleSymbol.valueDeclaration.getSourceFile()).map(moduleFileName => {
const sourceDirectory = getDirectoryPath(sourceFile.fileName);
const global = tryGetModuleNameFromAmbientModule(moduleSymbol)
|| tryGetModuleNameFromTypeRoots(options, host, getCanonicalFileName, moduleFileName)
|| tryGetModuleNameFromTypeRoots(options, host, getCanonicalFileName, moduleFileName, addJsExtension)
|| tryGetModuleNameAsNodeModule(options, moduleFileName, host, getCanonicalFileName, sourceDirectory)
|| rootDirs && tryGetModuleNameFromRootDirs(rootDirs, moduleFileName, sourceDirectory, getCanonicalFileName);
if (global) {
return [global];
}
const relativePath = removeExtensionAndIndexPostFix(getRelativePath(moduleFileName, sourceDirectory, getCanonicalFileName), options);
const relativePath = removeExtensionAndIndexPostFix(getRelativePath(moduleFileName, sourceDirectory, getCanonicalFileName), options, addJsExtension);
if (!baseUrl) {
return [relativePath];
}
@ -350,7 +355,7 @@ namespace ts.codefix {
return [relativePath];
}
const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, options);
const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, options, addJsExtension);
if (paths) {
const fromPaths = tryGetModuleNameFromPaths(removeFileExtension(relativeToBaseUrl), importRelativeToBaseUrl, paths);
if (fromPaths) {
@ -459,12 +464,13 @@ namespace ts.codefix {
host: GetEffectiveTypeRootsHost,
getCanonicalFileName: (file: string) => string,
moduleFileName: string,
addJsExtension: boolean,
): string | undefined {
const roots = getEffectiveTypeRoots(options, host);
return roots && firstDefined(roots, unNormalizedTypeRoot => {
const typeRoot = toPath(unNormalizedTypeRoot, /*basePath*/ undefined, getCanonicalFileName);
if (startsWith(moduleFileName, typeRoot)) {
return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), options);
return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), options, addJsExtension);
}
});
}
@ -598,9 +604,13 @@ namespace ts.codefix {
return firstDefined(rootDirs, rootDir => getRelativePathIfInDirectory(path, rootDir, getCanonicalFileName));
}
function removeExtensionAndIndexPostFix(fileName: string, options: CompilerOptions): string {
function removeExtensionAndIndexPostFix(fileName: string, options: CompilerOptions, addJsExtension: boolean): string {
const noExtension = removeFileExtension(fileName);
return getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeJs ? removeSuffix(noExtension, "/index") : noExtension;
return addJsExtension
? noExtension + ".js"
: getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeJs
? removeSuffix(noExtension, "/index")
: noExtension;
}
function getRelativePathIfInDirectory(path: string, directoryPath: string, getCanonicalFileName: GetCanonicalFileName): string | undefined {

View File

@ -0,0 +1,22 @@
/// <reference path="fourslash.ts" />
// @moduleResolution: node
// @noLib: true
// @Filename: /a.ts
////export function a() {}
// @Filename: /b.ts
////export function b() {}
// @Filename: /c.ts
////import * as g from "global"; // Global imports skipped
////import { a } from "./a.js";
////import { a as a2 } from "./a"; // Ignored, only the first relative import is considered
////[|b;|]
goTo.file("/c.ts");
verify.importFixAtPosition([
`import { b } from "./b.js";
b;`,
]);

View File

@ -11,7 +11,7 @@
////import { foo } from "link";
// @Filename: /b.ts
////[|foo/**/;|]
////[|foo;|]
// Uses "link" instead of "real" because `a` did.
goTo.file("/b.ts");