diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts
index a322e648f8b03..21482e577a383 100644
--- a/src/compiler/moduleSpecifiers.ts
+++ b/src/compiler/moduleSpecifiers.ts
@@ -275,6 +275,7 @@ function computeModuleSpecifiers(
// 4. Relative paths
let nodeModulesSpecifiers: string[] | undefined;
let pathsSpecifiers: string[] | undefined;
+ let redirectPathsSpecifiers: string[] | undefined;
let relativeSpecifiers: string[] | undefined;
for (const modulePath of modulePaths) {
const specifier = tryGetModuleNameAsNodeModule(modulePath, info, importingSourceFile, host, compilerOptions, userPreferences, /*packageNameOnly*/ undefined, options.overrideImportMode);
@@ -285,9 +286,23 @@ function computeModuleSpecifiers(
return nodeModulesSpecifiers!;
}
- if (!specifier && !modulePath.isRedirect) {
- const local = getLocalModuleSpecifier(modulePath.path, info, compilerOptions, host, options.overrideImportMode || importingSourceFile.impliedNodeFormat, preferences);
- if (pathIsBareSpecifier(local)) {
+ if (!specifier) {
+ const local = getLocalModuleSpecifier(
+ modulePath.path,
+ info,
+ compilerOptions,
+ host,
+ options.overrideImportMode || importingSourceFile.impliedNodeFormat,
+ preferences,
+ /*pathsOnly*/ modulePath.isRedirect,
+ );
+ if (!local) {
+ continue;
+ }
+ if (modulePath.isRedirect) {
+ redirectPathsSpecifiers = append(redirectPathsSpecifiers, local);
+ }
+ else if (pathIsBareSpecifier(local)) {
pathsSpecifiers = append(pathsSpecifiers, local);
}
else if (!importedFileIsInNodeModules || modulePath.isInNodeModules) {
@@ -306,8 +321,9 @@ function computeModuleSpecifiers(
}
return pathsSpecifiers?.length ? pathsSpecifiers :
+ redirectPathsSpecifiers?.length ? redirectPathsSpecifiers :
nodeModulesSpecifiers?.length ? nodeModulesSpecifiers :
- Debug.checkDefined(relativeSpecifiers);
+ Debug.checkDefined(relativeSpecifiers);
}
interface Info {
@@ -322,32 +338,42 @@ function getInfo(importingSourceFileName: Path, host: ModuleSpecifierResolutionH
return { getCanonicalFileName, importingSourceFileName, sourceDirectory };
}
-function getLocalModuleSpecifier(moduleFileName: string, info: Info, compilerOptions: CompilerOptions, host: ModuleSpecifierResolutionHost, importMode: ResolutionMode, { ending, relativePreference }: Preferences): string {
+function getLocalModuleSpecifier(moduleFileName: string, info: Info, compilerOptions: CompilerOptions, host: ModuleSpecifierResolutionHost, importMode: ResolutionMode, { ending, relativePreference }: Preferences): string;
+function getLocalModuleSpecifier(moduleFileName: string, info: Info, compilerOptions: CompilerOptions, host: ModuleSpecifierResolutionHost, importMode: ResolutionMode, { ending, relativePreference }: Preferences, pathsOnly?: boolean): string | undefined;
+function getLocalModuleSpecifier(moduleFileName: string, info: Info, compilerOptions: CompilerOptions, host: ModuleSpecifierResolutionHost, importMode: ResolutionMode, { ending, relativePreference }: Preferences, pathsOnly?: boolean): string | undefined {
const { baseUrl, paths, rootDirs } = compilerOptions;
+ if (pathsOnly && !paths) {
+ return undefined;
+ }
+
const { sourceDirectory, getCanonicalFileName } = info;
const relativePath = rootDirs && tryGetModuleNameFromRootDirs(rootDirs, moduleFileName, sourceDirectory, getCanonicalFileName, ending, compilerOptions) ||
removeExtensionAndIndexPostFix(ensurePathIsNonModuleName(getRelativePathFromDirectory(sourceDirectory, moduleFileName, getCanonicalFileName)), ending, compilerOptions);
if (!baseUrl && !paths || relativePreference === RelativePreference.Relative) {
- return relativePath;
+ return pathsOnly ? undefined : relativePath;
}
const baseDirectory = getNormalizedAbsolutePath(getPathsBasePath(compilerOptions, host) || baseUrl!, host.getCurrentDirectory());
const relativeToBaseUrl = getRelativePathIfInDirectory(moduleFileName, baseDirectory, getCanonicalFileName);
if (!relativeToBaseUrl) {
- return relativePath;
+ return pathsOnly ? undefined : relativePath;
}
const fromPaths = paths && tryGetModuleNameFromPaths(relativeToBaseUrl, paths, getAllowedEndings(ending, compilerOptions, importMode), host, compilerOptions);
- const nonRelative = fromPaths === undefined && baseUrl !== undefined ? removeExtensionAndIndexPostFix(relativeToBaseUrl, ending, compilerOptions) : fromPaths;
- if (!nonRelative) {
+ if (pathsOnly) {
+ return fromPaths;
+ }
+
+ const maybeNonRelative = fromPaths === undefined && baseUrl !== undefined ? removeExtensionAndIndexPostFix(relativeToBaseUrl, ending, compilerOptions) : fromPaths;
+ if (!maybeNonRelative) {
return relativePath;
}
- if (relativePreference === RelativePreference.NonRelative) {
- return nonRelative;
+ if (relativePreference === RelativePreference.NonRelative && !pathIsRelative(maybeNonRelative)) {
+ return maybeNonRelative;
}
- if (relativePreference === RelativePreference.ExternalNonRelative) {
+ if (relativePreference === RelativePreference.ExternalNonRelative && !pathIsRelative(maybeNonRelative)) {
const projectDirectory = compilerOptions.configFilePath ?
toPath(getDirectoryPath(compilerOptions.configFilePath), host.getCurrentDirectory(), info.getCanonicalFileName) :
info.getCanonicalFileName(host.getCurrentDirectory());
@@ -363,7 +389,7 @@ function getLocalModuleSpecifier(moduleFileName: string, info: Info, compilerOpt
// lib/ | (path crosses tsconfig.json)
// imported.ts <---
//
- return nonRelative;
+ return maybeNonRelative;
}
const nearestTargetPackageJson = getNearestAncestorDirectoryWithPackageJson(host, getDirectoryPath(modulePath));
@@ -378,16 +404,14 @@ function getLocalModuleSpecifier(moduleFileName: string, info: Info, compilerOpt
// package.json |
// component.ts <---
//
- return nonRelative;
+ return maybeNonRelative;
}
return relativePath;
}
- if (relativePreference !== RelativePreference.Shortest) Debug.assertNever(relativePreference);
-
// Prefer a relative import over a baseUrl import if it has fewer components.
- return isPathRelativeToParent(nonRelative) || countPathComponents(relativePath) < countPathComponents(nonRelative) ? relativePath : nonRelative;
+ return isPathRelativeToParent(maybeNonRelative) || countPathComponents(relativePath) < countPathComponents(maybeNonRelative) ? relativePath : maybeNonRelative;
}
/** @internal */
diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts
index 7e1e40fe17120..ac7c0c40ad3b5 100644
--- a/src/harness/fourslashImpl.ts
+++ b/src/harness/fourslashImpl.ts
@@ -3041,6 +3041,10 @@ export class TestState {
* @param fileName Path to file where error should be retrieved from.
*/
private getCodeFixes(fileName: string, errorCode?: number, preferences: ts.UserPreferences = ts.emptyOptions, position?: number): readonly ts.CodeFixAction[] {
+ if (this.testType === FourSlashTestType.Server) {
+ this.configure(preferences);
+ }
+
const diagnosticsForCodeFix = this.getDiagnostics(fileName, /*includeSuggestions*/ true).map(diagnostic => ({
start: diagnostic.start,
length: diagnostic.length,
diff --git a/tests/cases/fourslash/server/autoImportCrossProject_baseUrl_toDist.ts b/tests/cases/fourslash/server/autoImportCrossProject_baseUrl_toDist.ts
new file mode 100644
index 0000000000000..d6b6689d4e1a7
--- /dev/null
+++ b/tests/cases/fourslash/server/autoImportCrossProject_baseUrl_toDist.ts
@@ -0,0 +1,41 @@
+///
+
+// @Filename: /common/tsconfig.json
+//// {
+//// "compilerOptions": {
+//// "module": "commonjs",
+//// "outDir": "dist",
+//// "composite": true
+//// },
+//// "include": ["src"]
+//// }
+
+// @Filename: /common/src/MyModule.ts
+//// export function square(n: number) {
+//// return n * 2;
+//// }
+
+// @Filename: /web/tsconfig.json
+//// {
+//// "compilerOptions": {
+//// "module": "esnext",
+//// "moduleResolution": "node",
+//// "noEmit": true,
+//// "baseUrl": "."
+//// },
+//// "include": ["src"],
+//// "references": [{ "path": "../common" }]
+//// }
+
+// @Filename: /web/src/MyApp.ts
+//// import { square } from "../../common/dist/src/MyModule";
+
+// @Filename: /web/src/Helper.ts
+//// export function saveMe() {
+//// square/**/(2);
+//// }
+
+goTo.file("/web/src/Helper.ts");
+verify.importFixModuleSpecifiers("", ["../../common/src/MyModule"], {
+ importModuleSpecifierPreference: "non-relative"
+});
diff --git a/tests/cases/fourslash/server/autoImportCrossProject_paths_toDist2.ts b/tests/cases/fourslash/server/autoImportCrossProject_paths_toDist2.ts
new file mode 100644
index 0000000000000..4a7f7af5d14b3
--- /dev/null
+++ b/tests/cases/fourslash/server/autoImportCrossProject_paths_toDist2.ts
@@ -0,0 +1,43 @@
+///
+
+// @Filename: /common/tsconfig.json
+//// {
+//// "compilerOptions": {
+//// "module": "commonjs",
+//// "outDir": "dist",
+//// "composite": true
+//// },
+//// "include": ["src"]
+//// }
+
+// @Filename: /common/src/MyModule.ts
+//// export function square(n: number) {
+//// return n * 2;
+//// }
+
+// @Filename: /web/tsconfig.json
+//// {
+//// "compilerOptions": {
+//// "module": "esnext",
+//// "moduleResolution": "node",
+//// "noEmit": true,
+//// "paths": {
+//// "@common/*": ["../common/dist/src/*"]
+//// }
+//// },
+//// "include": ["src"],
+//// "references": [{ "path": "../common" }]
+//// }
+
+// @Filename: /web/src/MyApp.ts
+//// import { square } from "@common/MyModule";
+
+// @Filename: /web/src/Helper.ts
+//// export function saveMe() {
+//// square/**/(2);
+//// }
+
+goTo.file("/web/src/Helper.ts");
+verify.importFixModuleSpecifiers("", ["@common/MyModule"], {
+ importModuleSpecifierPreference: "non-relative"
+});