Skip to content

Commit

Permalink
Fix package.json auto imports for pnpm without project references (mi…
Browse files Browse the repository at this point in the history
…crosoft#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
  • Loading branch information
andrewbranch authored May 6, 2021
1 parent 71f338b commit 96c48b7
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 15 deletions.
17 changes: 12 additions & 5 deletions src/compiler/moduleNameResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down
11 changes: 10 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down
29 changes: 24 additions & 5 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(
Expand Down Expand Up @@ -6149,6 +6151,8 @@ namespace ts {
getSymlinkedFiles(): ReadonlyESMap<Path, string> | 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 {
Expand All @@ -6172,16 +6176,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<readonly [string, string]>(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(
Expand Down
12 changes: 8 additions & 4 deletions src/server/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
}
Expand Down
21 changes: 21 additions & 0 deletions tests/cases/fourslash/server/autoImportProvider_pnpm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/// <reference path="../fourslash.ts" />

// @Filename: /tsconfig.json
//// { "compilerOptions": { "module": "commonjs" } }

// @Filename: /package.json
//// { "dependencies": { "mobx": "*" } }

// @Filename: /node_modules/.pnpm/[email protected]/node_modules/mobx/package.json
//// { "types": "dist/mobx.d.ts" }

// @Filename: /node_modules/.pnpm/[email protected]/node_modules/mobx/dist/mobx.d.ts
//// export declare function autorun(): void;

// @Filename: /index.ts
//// autorun/**/

// @link: /node_modules/.pnpm/[email protected]/node_modules/mobx -> /node_modules/mobx

goTo.marker("");
verify.importFixAtPosition([`import { autorun } from "mobx";\r\n\r\nautorun`]);

0 comments on commit 96c48b7

Please sign in to comment.