From 06e459772a0fc5a00a37d4f7f506bd28cbebcf67 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 20 Oct 2022 11:44:23 -0700 Subject: [PATCH 1/2] Generate shortest rootDirs module specifier instead of first possible --- src/compiler/core.ts | 10 +++++++-- src/compiler/moduleSpecifiers.ts | 23 ++++++++++++++------- tests/cases/fourslash/autoImportRootDirs.ts | 17 +++++++++++++++ 3 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 tests/cases/fourslash/autoImportRootDirs.ts diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 698f7bb2fb705..a1d32c15665a3 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1937,8 +1937,14 @@ namespace ts { return compareValues(a?.start, b?.start) || compareValues(a?.length, b?.length); } - export function min(a: T, b: T, compare: Comparer): T { - return compare(a, b) === Comparison.LessThan ? a : b; + export function min(a: readonly T[], compare: Comparer): T | undefined; + export function min(a: T, b: T, compare: Comparer): T; + export function min(a: T | readonly T[], b: T | Comparer, compare?: Comparer): T | undefined { + if (compare !== undefined) { + return compare(a as T, b as T) === Comparison.LessThan ? a as T : b as T; + } + compare = b as Comparer; + return reduceLeft(a as readonly T[], (x, y) => compare!(x, y) === Comparison.LessThan ? x : y); } /** diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index 32640fcb81579..2327f8924b8e6 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -726,16 +726,23 @@ namespace ts.moduleSpecifiers { } function tryGetModuleNameFromRootDirs(rootDirs: readonly string[], moduleFileName: string, sourceDirectory: string, getCanonicalFileName: (file: string) => string, ending: Ending, compilerOptions: CompilerOptions): string | undefined { - const normalizedTargetPath = getPathRelativeToRootDirs(moduleFileName, rootDirs, getCanonicalFileName); - if (normalizedTargetPath === undefined) { + const normalizedTargetPaths = getPathsRelativeToRootDirs(moduleFileName, rootDirs, getCanonicalFileName); + if (normalizedTargetPaths === undefined) { + return undefined; + } + + const normalizedSourcePaths = getPathsRelativeToRootDirs(sourceDirectory, rootDirs, getCanonicalFileName); + const relativePaths = flatMap(normalizedSourcePaths, sourcePath => { + return map(normalizedTargetPaths, targetPath => ensurePathIsNonModuleName(getRelativePathFromDirectory(sourcePath, targetPath, getCanonicalFileName))); + }); + const shortest = min(relativePaths, compareNumberOfDirectorySeparators); + if (!shortest) { return undefined; } - const normalizedSourcePath = getPathRelativeToRootDirs(sourceDirectory, rootDirs, getCanonicalFileName); - const relativePath = normalizedSourcePath !== undefined ? ensurePathIsNonModuleName(getRelativePathFromDirectory(normalizedSourcePath, normalizedTargetPath, getCanonicalFileName)) : normalizedTargetPath; return getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeJs - ? removeExtensionAndIndexPostFix(relativePath, ending, compilerOptions) - : removeFileExtension(relativePath); + ? removeExtensionAndIndexPostFix(shortest, ending, compilerOptions) + : removeFileExtension(shortest); } function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCanonicalFileName, sourceDirectory }: Info, importingSourceFile: SourceFile , host: ModuleSpecifierResolutionHost, options: CompilerOptions, userPreferences: UserPreferences, packageNameOnly?: boolean, overrideMode?: ModuleKind.ESNext | ModuleKind.CommonJS): string | undefined { @@ -882,8 +889,8 @@ namespace ts.moduleSpecifiers { } } - function getPathRelativeToRootDirs(path: string, rootDirs: readonly string[], getCanonicalFileName: GetCanonicalFileName): string | undefined { - return firstDefined(rootDirs, rootDir => { + function getPathsRelativeToRootDirs(path: string, rootDirs: readonly string[], getCanonicalFileName: GetCanonicalFileName): string[] | undefined { + return mapDefined(rootDirs, rootDir => { const relativePath = getRelativePathIfInDirectory(path, rootDir, getCanonicalFileName); return relativePath !== undefined && isPathRelativeToParent(relativePath) ? undefined : relativePath; }); diff --git a/tests/cases/fourslash/autoImportRootDirs.ts b/tests/cases/fourslash/autoImportRootDirs.ts new file mode 100644 index 0000000000000..7ca66d2d7d1fe --- /dev/null +++ b/tests/cases/fourslash/autoImportRootDirs.ts @@ -0,0 +1,17 @@ +/// + +// @Filename: /tsconfig.json +//// { +//// "compilerOptions": { +//// "module": "commonjs", +//// "rootDirs": [".", "./some/other/root"] +//// } +//// } + +// @Filename: /some/other/root/types.ts +//// export type Something = {}; + +// @Filename: /index.ts +//// const s: Something/**/ + +verify.importFixModuleSpecifiers("", ["./types"]); From 316089d368a1bdf870d7eaab6b29691dab3c8b0e Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 20 Oct 2022 11:50:00 -0700 Subject: [PATCH 2/2] Simplify `min` --- src/compiler/core.ts | 12 ++++-------- src/services/patternMatcher.ts | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index a1d32c15665a3..870892d864031 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1937,14 +1937,10 @@ namespace ts { return compareValues(a?.start, b?.start) || compareValues(a?.length, b?.length); } - export function min(a: readonly T[], compare: Comparer): T | undefined; - export function min(a: T, b: T, compare: Comparer): T; - export function min(a: T | readonly T[], b: T | Comparer, compare?: Comparer): T | undefined { - if (compare !== undefined) { - return compare(a as T, b as T) === Comparison.LessThan ? a as T : b as T; - } - compare = b as Comparer; - return reduceLeft(a as readonly T[], (x, y) => compare!(x, y) === Comparison.LessThan ? x : y); + export function min(items: readonly [T, ...T[]], compare: Comparer): T; + export function min(items: readonly T[], compare: Comparer): T | undefined; + export function min(items: readonly T[], compare: Comparer): T | undefined { + return reduceLeft(items, (x, y) => compare(x, y) === Comparison.LessThan ? x : y); } /** diff --git a/src/services/patternMatcher.ts b/src/services/patternMatcher.ts index b0071f7124098..66e4a6217933a 100644 --- a/src/services/patternMatcher.ts +++ b/src/services/patternMatcher.ts @@ -259,7 +259,7 @@ namespace ts { } function betterMatch(a: PatternMatch | undefined, b: PatternMatch | undefined): PatternMatch | undefined { - return min(a, b, compareMatches); + return min([a, b], compareMatches); } function compareMatches(a: PatternMatch | undefined, b: PatternMatch | undefined): Comparison { return a === undefined ? Comparison.GreaterThan : b === undefined ? Comparison.LessThan