From c67c68e1490aa7248b44fea0aaa189ce13ca430b Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 23 Sep 2019 13:15:40 -0700 Subject: [PATCH] Sort the paths for module specifier by closeness to importing file path Fixes #32970 --- src/compiler/moduleSpecifiers.ts | 39 ++++++++++++++++++- ...gh-source-and-another-symlinked-package.js | 11 +++++- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index cd566c0e6e840..fbb832400c24f 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -186,6 +186,18 @@ namespace ts.moduleSpecifiers { return result; } + function numberOfDirectorySeparators(str: string) { + const match = str.match(/\//g); + return match ? match.length : 0; + } + + function comparePathsByNumberOfDirectrorySeparators(a: string, b: string) { + return compareValues( + numberOfDirectorySeparators(a), + numberOfDirectorySeparators(b) + ); + } + /** * Looks for existing imports that use symlinks to this module. * Symlinks will be returned first so they are preferred over the real path. @@ -214,7 +226,32 @@ namespace ts.moduleSpecifiers { } }); result.push(...targets); - return result; + if (result.length < 2) return result; + + // Sort by paths closest to importing file Name directory + const allFileNames = arrayToMap(result, identity, getCanonicalFileName); + const sortedPaths: string[] = []; + for ( + let directory = getDirectoryPath(toPath(importingFileName, cwd, getCanonicalFileName)); + allFileNames.size !== 0; + directory = getDirectoryPath(directory) + ) { + const directoryStart = ensureTrailingDirectorySeparator(directory); + let pathsInDirectory: string[] | undefined; + allFileNames.forEach((canonicalFileName, fileName) => { + if (startsWith(canonicalFileName, directoryStart)) { + (pathsInDirectory || (pathsInDirectory = [])).push(fileName); + allFileNames.delete(fileName); + } + }); + if (pathsInDirectory) { + if (pathsInDirectory.length > 1) { + pathsInDirectory.sort(comparePathsByNumberOfDirectrorySeparators); + } + sortedPaths.push(...pathsInDirectory); + } + } + return sortedPaths; } function tryGetModuleNameFromAmbientModule(moduleSymbol: Symbol): string | undefined { diff --git a/tests/baselines/reference/tsc/declarationEmit/initial-build/when-same-version-is-referenced-through-source-and-another-symlinked-package.js b/tests/baselines/reference/tsc/declarationEmit/initial-build/when-same-version-is-referenced-through-source-and-another-symlinked-package.js index ec612b65f170d..5db17df5e9814 100644 --- a/tests/baselines/reference/tsc/declarationEmit/initial-build/when-same-version-is-referenced-through-source-and-another-symlinked-package.js +++ b/tests/baselines/reference/tsc/declarationEmit/initial-build/when-same-version-is-referenced-through-source-and-another-symlinked-package.js @@ -1,13 +1,20 @@ //// [/lib/initial-buildOutput.txt] /lib/tsc -p plugin-one --listFiles -plugin-one/action.ts(4,14): error TS2742: The inferred type of 'actions' cannot be named without a reference to 'plugin-two/node_modules/typescript-fsa'. This is likely not portable. A type annotation is necessary. /lib/lib.d.ts /plugin-one/node_modules/typescript-fsa/index.d.ts /plugin-one/action.ts /plugin-two/node_modules/typescript-fsa/index.d.ts -> /plugin-one/node_modules/typescript-fsa/index.d.ts /plugin-two/index.d.ts /plugin-one/index.ts -exitCode:: 1 +exitCode:: 0 + + +//// [/plugin-one/action.d.ts] +export declare const actions: { + featureOne: import("typescript-fsa").ActionCreator<{ + route: string; + }>; +}; //// [/plugin-one/action.js]