Skip to content

Commit 96c48b7

Browse files
authored
Fix package.json auto imports for pnpm without project references (microsoft#43892)
* Fix package.json auto imports for pnpm without project references * Make property optional * Revert unnecessary unnittest change * Set symlinked files when setting symlinked directories * Update `typeDirectiveIsEqualTo` * Consider symlinks found during type reference directive resolution into `discoverProbableSymlinks` * Rename `originalFileName` to `originalPath`, make internal
1 parent 71f338b commit 96c48b7

File tree

5 files changed

+75
-15
lines changed

5 files changed

+75
-15
lines changed

src/compiler/moduleNameResolver.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,12 @@ namespace ts {
4343
packageId: PackageId | undefined;
4444
/**
4545
* When the resolved is not created from cache, the value is
46-
* - string if original Path if it is symbolic link to the resolved path
47-
* - undefined if path is not a symbolic link
46+
* - string if it is symbolic link to the resolved `path`
47+
* - undefined if `path` is not a symbolic link
4848
* When the resolved is created using value from cache of ResolvedModuleWithFailedLookupLocations, the value is:
49-
* - string if original Path if it is symbolic link to the resolved path
50-
* - true if path is not a symbolic link - this indicates that the originalPath calculation is already done and needs to be skipped
49+
* - string if it is symbolic link to the resolved `path`
50+
* - true if `path` is not a symbolic link - this indicates that the `originalPath` calculation is already done and needs to be skipped
51+
* Note: This is a file name with preserved original casing, not a normalized `Path`.
5152
*/
5253
originalPath?: string | true;
5354
}
@@ -339,7 +340,13 @@ namespace ts {
339340
if (resolved) {
340341
const { fileName, packageId } = resolved;
341342
const resolvedFileName = options.preserveSymlinks ? fileName : realPath(fileName, host, traceEnabled);
342-
resolvedTypeReferenceDirective = { primary, resolvedFileName, packageId, isExternalLibraryImport: pathContainsNodeModules(fileName) };
343+
resolvedTypeReferenceDirective = {
344+
primary,
345+
resolvedFileName,
346+
originalPath: fileName === resolvedFileName ? undefined : fileName,
347+
packageId,
348+
isExternalLibraryImport: pathContainsNodeModules(fileName),
349+
};
343350
}
344351
result = { resolvedTypeReferenceDirective, failedLookupLocations };
345352
perFolderCache?.set(typeReferenceDirectiveName, result);

src/compiler/types.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -6407,7 +6407,10 @@ namespace ts {
64076407
* If changing this, remember to change `moduleResolutionIsEqualTo`.
64086408
*/
64096409
export interface ResolvedModuleFull extends ResolvedModule {
6410-
/* @internal */
6410+
/**
6411+
* @internal
6412+
* This is a file name with preserved original casing, not a normalized `Path`.
6413+
*/
64116414
readonly originalPath?: string;
64126415
/**
64136416
* Extension of resolvedFileName. This must match what's at the end of resolvedFileName.
@@ -6458,6 +6461,12 @@ namespace ts {
64586461
primary: boolean;
64596462
// The location of the .d.ts file we located, or undefined if resolution failed
64606463
resolvedFileName: string | undefined;
6464+
/**
6465+
* @internal
6466+
* The location of the symlink to the .d.ts file we found, if `resolvedFileName` was the realpath.
6467+
* This is a file name with preserved original casing, not a normalized `Path`.
6468+
*/
6469+
originalPath?: string;
64616470
packageId?: PackageId;
64626471
/** True if `resolvedFileName` comes from `node_modules`. */
64636472
isExternalLibraryImport?: boolean;

src/compiler/utilities.ts

+24-5
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,9 @@ namespace ts {
205205
}
206206

207207
export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirective, newResolution: ResolvedTypeReferenceDirective): boolean {
208-
return oldResolution.resolvedFileName === newResolution.resolvedFileName && oldResolution.primary === newResolution.primary;
208+
return oldResolution.resolvedFileName === newResolution.resolvedFileName
209+
&& oldResolution.primary === newResolution.primary
210+
&& oldResolution.originalPath === newResolution.originalPath;
209211
}
210212

211213
export function hasChangesInResolutions<T>(
@@ -6149,6 +6151,8 @@ namespace ts {
61496151
getSymlinkedFiles(): ReadonlyESMap<Path, string> | undefined;
61506152
setSymlinkedDirectory(symlink: string, real: SymlinkedDirectory | false): void;
61516153
setSymlinkedFile(symlinkPath: Path, real: string): void;
6154+
/*@internal*/
6155+
setSymlinkedDirectoryFromSymlinkedFile(symlink: string, real: string): void;
61526156
}
61536157

61546158
export function createSymlinkCache(cwd: string, getCanonicalFileName: GetCanonicalFileName): SymlinkCache {
@@ -6172,16 +6176,31 @@ namespace ts {
61726176
}
61736177
(symlinkedDirectories || (symlinkedDirectories = new Map())).set(symlinkPath, real);
61746178
}
6175-
}
6179+
},
6180+
setSymlinkedDirectoryFromSymlinkedFile(symlink, real) {
6181+
this.setSymlinkedFile(toPath(symlink, cwd, getCanonicalFileName), real);
6182+
const [commonResolved, commonOriginal] = guessDirectorySymlink(real, symlink, cwd, getCanonicalFileName) || emptyArray;
6183+
if (commonResolved && commonOriginal) {
6184+
this.setSymlinkedDirectory(commonOriginal, {
6185+
real: commonResolved,
6186+
realPath: toPath(commonResolved, cwd, getCanonicalFileName),
6187+
});
6188+
}
6189+
},
61766190
};
61776191
}
61786192

61796193
export function discoverProbableSymlinks(files: readonly SourceFile[], getCanonicalFileName: GetCanonicalFileName, cwd: string): SymlinkCache {
61806194
const cache = createSymlinkCache(cwd, getCanonicalFileName);
6181-
const symlinks = flatten<readonly [string, string]>(mapDefined(files, sf =>
6182-
sf.resolvedModules && compact(arrayFrom(mapIterator(sf.resolvedModules.values(), res =>
6183-
res && res.originalPath && res.resolvedFileName !== res.originalPath ? [res.resolvedFileName, res.originalPath] as const : undefined)))));
6195+
const symlinks = flatMap(files, sf => {
6196+
const pairs = sf.resolvedModules && arrayFrom(mapDefinedIterator(sf.resolvedModules.values(), res =>
6197+
res?.originalPath ? [res.resolvedFileName, res.originalPath] as const : undefined));
6198+
return concatenate(pairs, sf.resolvedTypeReferenceDirectiveNames && arrayFrom(mapDefinedIterator(sf.resolvedTypeReferenceDirectiveNames.values(), res =>
6199+
res?.originalPath && res.resolvedFileName ? [res.resolvedFileName, res.originalPath] as const : undefined)));
6200+
});
6201+
61846202
for (const [resolvedPath, originalPath] of symlinks) {
6203+
cache.setSymlinkedFile(toPath(originalPath, cwd, getCanonicalFileName), resolvedPath);
61856204
const [commonResolved, commonOriginal] = guessDirectorySymlink(resolvedPath, originalPath, cwd, getCanonicalFileName) || emptyArray;
61866205
if (commonResolved && commonOriginal) {
61876206
cache.setSymlinkedDirectory(

src/server/project.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -1901,16 +1901,20 @@ namespace ts.server {
19011901
compilerOptions,
19021902
moduleResolutionHost));
19031903

1904+
const program = hostProject.getCurrentProgram()!;
1905+
const symlinkCache = hostProject.getSymlinkCache();
19041906
for (const resolution of resolutions) {
19051907
if (!resolution.resolvedTypeReferenceDirective?.resolvedFileName) continue;
1906-
const { resolvedFileName } = resolution.resolvedTypeReferenceDirective;
1907-
const fileName = moduleResolutionHost.realpath?.(resolvedFileName) || resolvedFileName;
1908-
if (!hostProject.getCurrentProgram()!.getSourceFile(fileName) && !hostProject.getCurrentProgram()!.getSourceFile(resolvedFileName)) {
1909-
rootNames = append(rootNames, fileName);
1908+
const { resolvedFileName, originalPath } = resolution.resolvedTypeReferenceDirective;
1909+
if (!program.getSourceFile(resolvedFileName) && (!originalPath || !program.getSourceFile(originalPath))) {
1910+
rootNames = append(rootNames, resolvedFileName);
19101911
// Avoid creating a large project that would significantly slow down time to editor interactivity
19111912
if (dependencySelection === PackageJsonAutoImportPreference.Auto && rootNames.length > this.maxDependencies) {
19121913
return ts.emptyArray;
19131914
}
1915+
if (originalPath) {
1916+
symlinkCache.setSymlinkedDirectoryFromSymlinkedFile(originalPath, resolvedFileName);
1917+
}
19141918
}
19151919
}
19161920
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/// <reference path="../fourslash.ts" />
2+
3+
// @Filename: /tsconfig.json
4+
//// { "compilerOptions": { "module": "commonjs" } }
5+
6+
// @Filename: /package.json
7+
//// { "dependencies": { "mobx": "*" } }
8+
9+
// @Filename: /node_modules/.pnpm/[email protected]/node_modules/mobx/package.json
10+
//// { "types": "dist/mobx.d.ts" }
11+
12+
// @Filename: /node_modules/.pnpm/[email protected]/node_modules/mobx/dist/mobx.d.ts
13+
//// export declare function autorun(): void;
14+
15+
// @Filename: /index.ts
16+
//// autorun/**/
17+
18+
// @link: /node_modules/.pnpm/[email protected]/node_modules/mobx -> /node_modules/mobx
19+
20+
goTo.marker("");
21+
verify.importFixAtPosition([`import { autorun } from "mobx";\r\n\r\nautorun`]);

0 commit comments

Comments
 (0)