diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 93cf570d7962c..8a8cb699a37bd 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -43,11 +43,12 @@ namespace ts { packageId: PackageId | undefined; /** * When the resolved is not created from cache, the value is - * - string if original Path if it is symbolic link to the resolved path - * - undefined if path is not a symbolic link + * - string if it is symbolic link to the resolved `path` + * - undefined if `path` is not a symbolic link * When the resolved is created using value from cache of ResolvedModuleWithFailedLookupLocations, the value is: - * - string if original Path if it is symbolic link to the resolved path - * - true if path is not a symbolic link - this indicates that the originalPath calculation is already done and needs to be skipped + * - string if it is symbolic link to the resolved `path` + * - true if `path` is not a symbolic link - this indicates that the `originalPath` calculation is already done and needs to be skipped + * Note: This is a file name with preserved original casing, not a normalized `Path`. */ originalPath?: string | true; } @@ -339,7 +340,13 @@ namespace ts { if (resolved) { const { fileName, packageId } = resolved; const resolvedFileName = options.preserveSymlinks ? fileName : realPath(fileName, host, traceEnabled); - resolvedTypeReferenceDirective = { primary, resolvedFileName, packageId, isExternalLibraryImport: pathContainsNodeModules(fileName) }; + resolvedTypeReferenceDirective = { + primary, + resolvedFileName, + originalPath: fileName === resolvedFileName ? undefined : fileName, + packageId, + isExternalLibraryImport: pathContainsNodeModules(fileName), + }; } result = { resolvedTypeReferenceDirective, failedLookupLocations }; perFolderCache?.set(typeReferenceDirectiveName, result); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f7183d7dd6c27..e352625ab043f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6407,7 +6407,10 @@ namespace ts { * If changing this, remember to change `moduleResolutionIsEqualTo`. */ export interface ResolvedModuleFull extends ResolvedModule { - /* @internal */ + /** + * @internal + * This is a file name with preserved original casing, not a normalized `Path`. + */ readonly originalPath?: string; /** * Extension of resolvedFileName. This must match what's at the end of resolvedFileName. @@ -6458,6 +6461,12 @@ namespace ts { primary: boolean; // The location of the .d.ts file we located, or undefined if resolution failed resolvedFileName: string | undefined; + /** + * @internal + * The location of the symlink to the .d.ts file we found, if `resolvedFileName` was the realpath. + * This is a file name with preserved original casing, not a normalized `Path`. + */ + originalPath?: string; packageId?: PackageId; /** True if `resolvedFileName` comes from `node_modules`. */ isExternalLibraryImport?: boolean; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 9bbdea8a6ac2d..e99d1b81ef81b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -205,7 +205,9 @@ namespace ts { } export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirective, newResolution: ResolvedTypeReferenceDirective): boolean { - return oldResolution.resolvedFileName === newResolution.resolvedFileName && oldResolution.primary === newResolution.primary; + return oldResolution.resolvedFileName === newResolution.resolvedFileName + && oldResolution.primary === newResolution.primary + && oldResolution.originalPath === newResolution.originalPath; } export function hasChangesInResolutions( @@ -6145,6 +6147,8 @@ namespace ts { getSymlinkedFiles(): ReadonlyESMap | undefined; setSymlinkedDirectory(symlink: string, real: SymlinkedDirectory | false): void; setSymlinkedFile(symlinkPath: Path, real: string): void; + /*@internal*/ + setSymlinkedDirectoryFromSymlinkedFile(symlink: string, real: string): void; } export function createSymlinkCache(cwd: string, getCanonicalFileName: GetCanonicalFileName): SymlinkCache { @@ -6168,16 +6172,31 @@ namespace ts { } (symlinkedDirectories || (symlinkedDirectories = new Map())).set(symlinkPath, real); } - } + }, + setSymlinkedDirectoryFromSymlinkedFile(symlink, real) { + this.setSymlinkedFile(toPath(symlink, cwd, getCanonicalFileName), real); + const [commonResolved, commonOriginal] = guessDirectorySymlink(real, symlink, cwd, getCanonicalFileName) || emptyArray; + if (commonResolved && commonOriginal) { + this.setSymlinkedDirectory(commonOriginal, { + real: commonResolved, + realPath: toPath(commonResolved, cwd, getCanonicalFileName), + }); + } + }, }; } export function discoverProbableSymlinks(files: readonly SourceFile[], getCanonicalFileName: GetCanonicalFileName, cwd: string): SymlinkCache { const cache = createSymlinkCache(cwd, getCanonicalFileName); - const symlinks = flatten(mapDefined(files, sf => - sf.resolvedModules && compact(arrayFrom(mapIterator(sf.resolvedModules.values(), res => - res && res.originalPath && res.resolvedFileName !== res.originalPath ? [res.resolvedFileName, res.originalPath] as const : undefined))))); + const symlinks = flatMap(files, sf => { + const pairs = sf.resolvedModules && arrayFrom(mapDefinedIterator(sf.resolvedModules.values(), res => + res?.originalPath ? [res.resolvedFileName, res.originalPath] as const : undefined)); + return concatenate(pairs, sf.resolvedTypeReferenceDirectiveNames && arrayFrom(mapDefinedIterator(sf.resolvedTypeReferenceDirectiveNames.values(), res => + res?.originalPath && res.resolvedFileName ? [res.resolvedFileName, res.originalPath] as const : undefined))); + }); + for (const [resolvedPath, originalPath] of symlinks) { + cache.setSymlinkedFile(toPath(originalPath, cwd, getCanonicalFileName), resolvedPath); const [commonResolved, commonOriginal] = guessDirectorySymlink(resolvedPath, originalPath, cwd, getCanonicalFileName) || emptyArray; if (commonResolved && commonOriginal) { cache.setSymlinkedDirectory( diff --git a/src/server/project.ts b/src/server/project.ts index 1d1bcd1e7f9c4..60d045ea8ee0b 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -1901,16 +1901,20 @@ namespace ts.server { compilerOptions, moduleResolutionHost)); + const program = hostProject.getCurrentProgram()!; + const symlinkCache = hostProject.getSymlinkCache(); for (const resolution of resolutions) { if (!resolution.resolvedTypeReferenceDirective?.resolvedFileName) continue; - const { resolvedFileName } = resolution.resolvedTypeReferenceDirective; - const fileName = moduleResolutionHost.realpath?.(resolvedFileName) || resolvedFileName; - if (!hostProject.getCurrentProgram()!.getSourceFile(fileName) && !hostProject.getCurrentProgram()!.getSourceFile(resolvedFileName)) { - rootNames = append(rootNames, fileName); + const { resolvedFileName, originalPath } = resolution.resolvedTypeReferenceDirective; + if (!program.getSourceFile(resolvedFileName) && (!originalPath || !program.getSourceFile(originalPath))) { + rootNames = append(rootNames, resolvedFileName); // Avoid creating a large project that would significantly slow down time to editor interactivity if (dependencySelection === PackageJsonAutoImportPreference.Auto && rootNames.length > this.maxDependencies) { return ts.emptyArray; } + if (originalPath) { + symlinkCache.setSymlinkedDirectoryFromSymlinkedFile(originalPath, resolvedFileName); + } } } } diff --git a/tests/cases/fourslash/server/autoImportProvider_pnpm.ts b/tests/cases/fourslash/server/autoImportProvider_pnpm.ts new file mode 100644 index 0000000000000..67a8b613420ba --- /dev/null +++ b/tests/cases/fourslash/server/autoImportProvider_pnpm.ts @@ -0,0 +1,21 @@ +/// + +// @Filename: /tsconfig.json +//// { "compilerOptions": { "module": "commonjs" } } + +// @Filename: /package.json +//// { "dependencies": { "mobx": "*" } } + +// @Filename: /node_modules/.pnpm/mobx@6.0.4/node_modules/mobx/package.json +//// { "types": "dist/mobx.d.ts" } + +// @Filename: /node_modules/.pnpm/mobx@6.0.4/node_modules/mobx/dist/mobx.d.ts +//// export declare function autorun(): void; + +// @Filename: /index.ts +//// autorun/**/ + +// @link: /node_modules/.pnpm/mobx@6.0.4/node_modules/mobx -> /node_modules/mobx + +goTo.marker(""); +verify.importFixAtPosition([`import { autorun } from "mobx";\r\n\r\nautorun`]);