Skip to content

Commit a673958

Browse files
authored
Check autoImportExcludePatterns against symlink paths (#57033)
1 parent cf33fd0 commit a673958

File tree

7 files changed

+2288
-8
lines changed

7 files changed

+2288
-8
lines changed

src/services/exportInfoMap.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
createMultiMap,
99
Debug,
1010
emptyArray,
11+
ensureTrailingDirectorySeparator,
1112
findIndex,
1213
firstDefined,
1314
forEachAncestorDirectory,
@@ -43,6 +44,7 @@ import {
4344
nodeModulesPathPart,
4445
PackageJsonImportFilter,
4546
Path,
47+
pathContainsNodeModules,
4648
Program,
4749
skipAlias,
4850
skipOuterExpressions,
@@ -427,12 +429,12 @@ export function forEachExternalModuleToImportFrom(
427429
return pattern ? getRegexFromPattern(pattern, useCaseSensitiveFileNames) : undefined;
428430
});
429431

430-
forEachExternalModule(program.getTypeChecker(), program.getSourceFiles(), excludePatterns, (module, file) => cb(module, file, program, /*isFromPackageJson*/ false));
432+
forEachExternalModule(program.getTypeChecker(), program.getSourceFiles(), excludePatterns, host, (module, file) => cb(module, file, program, /*isFromPackageJson*/ false));
431433
const autoImportProvider = useAutoImportProvider && host.getPackageJsonAutoImportProvider?.();
432434
if (autoImportProvider) {
433435
const start = timestamp();
434436
const checker = program.getTypeChecker();
435-
forEachExternalModule(autoImportProvider.getTypeChecker(), autoImportProvider.getSourceFiles(), excludePatterns, (module, file) => {
437+
forEachExternalModule(autoImportProvider.getTypeChecker(), autoImportProvider.getSourceFiles(), excludePatterns, host, (module, file) => {
436438
if (file && !program.getSourceFile(file.fileName) || !file && !checker.resolveName(module.name, /*location*/ undefined, SymbolFlags.Module, /*excludeGlobals*/ false)) {
437439
// The AutoImportProvider filters files already in the main program out of its *root* files,
438440
// but non-root files can still be present in both programs, and already in the export info map
@@ -445,15 +447,30 @@ export function forEachExternalModuleToImportFrom(
445447
}
446448
}
447449

448-
function forEachExternalModule(checker: TypeChecker, allSourceFiles: readonly SourceFile[], excludePatterns: readonly RegExp[] | undefined, cb: (module: Symbol, sourceFile: SourceFile | undefined) => void) {
449-
const isExcluded = excludePatterns && ((fileName: string) => excludePatterns.some(p => p.test(fileName)));
450+
function forEachExternalModule(checker: TypeChecker, allSourceFiles: readonly SourceFile[], excludePatterns: readonly RegExp[] | undefined, host: LanguageServiceHost, cb: (module: Symbol, sourceFile: SourceFile | undefined) => void) {
451+
const realpathsWithSymlinks = host.getSymlinkCache?.().getSymlinkedDirectoriesByRealpath();
452+
const isExcluded = excludePatterns && (({ fileName, path }: SourceFile) => {
453+
if (excludePatterns.some(p => p.test(fileName))) return true;
454+
if (realpathsWithSymlinks?.size && pathContainsNodeModules(fileName)) {
455+
let dir = getDirectoryPath(fileName);
456+
return forEachAncestorDirectory(getDirectoryPath(path), dirPath => {
457+
const symlinks = realpathsWithSymlinks.get(ensureTrailingDirectorySeparator(dirPath));
458+
if (symlinks) {
459+
return symlinks.some(s => excludePatterns.some(p => p.test(fileName.replace(dir, s))));
460+
}
461+
dir = getDirectoryPath(dir);
462+
}) ?? false;
463+
}
464+
return false;
465+
});
466+
450467
for (const ambient of checker.getAmbientModules()) {
451-
if (!ambient.name.includes("*") && !(excludePatterns && ambient.declarations?.every(d => isExcluded!(d.getSourceFile().fileName)))) {
468+
if (!ambient.name.includes("*") && !(excludePatterns && ambient.declarations?.every(d => isExcluded!(d.getSourceFile())))) {
452469
cb(ambient, /*sourceFile*/ undefined);
453470
}
454471
}
455472
for (const sourceFile of allSourceFiles) {
456-
if (isExternalOrCommonJsModule(sourceFile) && !isExcluded?.(sourceFile.fileName)) {
473+
if (isExternalOrCommonJsModule(sourceFile) && !isExcluded?.(sourceFile)) {
457474
cb(checker.getMergedSymbol(sourceFile.symbol), sourceFile);
458475
}
459476
}

tests/baselines/reference/tsserver/fourslashServer/autoImportFileExcludePatterns1.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export * from "./clients/s3";
2222
{ "name": "aws-sdk", "version": "2.0.0", "main": "index.js" }
2323

2424
//// [/project/package.json]
25-
{ "dependencies": "aws-sdk" }
25+
{ "dependencies": { "aws-sdk": "*" } }
2626

2727

2828
Info seq [hh:mm:ss:mss] request:
@@ -113,13 +113,35 @@ Info seq [hh:mm:ss:mss] Files (4)
113113

114114
Info seq [hh:mm:ss:mss] -----------------------------------------------
115115
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /project/package.json 250 undefined WatchType: package.json file
116+
Info seq [hh:mm:ss:mss] AutoImportProviderProject: found 1 root files in 1 dependencies in * ms
117+
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /project/node_modules/aws-sdk/index.d.ts 500 undefined WatchType: Closed Script info
118+
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /dev/null/autoImportProviderProject1*
119+
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /project/node_modules/aws-sdk/package.json 2000 undefined Project: /dev/null/autoImportProviderProject1* WatchType: File location affecting resolution
120+
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /project/node_modules/aws-sdk/clients/s3.d.ts 500 undefined WatchType: Closed Script info
121+
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /dev/null/autoImportProviderProject1* Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
122+
Info seq [hh:mm:ss:mss] Project '/dev/null/autoImportProviderProject1*' (AutoImportProvider)
123+
Info seq [hh:mm:ss:mss] Files (2)
124+
/project/node_modules/aws-sdk/clients/s3.d.ts Text-1 "export declare class S3 {}"
125+
/project/node_modules/aws-sdk/index.d.ts Text-1 "export * from \"./clients/s3\";"
126+
127+
128+
node_modules/aws-sdk/clients/s3.d.ts
129+
Imported via "./clients/s3" from file 'node_modules/aws-sdk/index.d.ts' with packageId 'aws-sdk/clients/s3.d.ts@2.0.0'
130+
node_modules/aws-sdk/index.d.ts
131+
Root file specified for compilation
132+
133+
Info seq [hh:mm:ss:mss] -----------------------------------------------
116134
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
117135
Info seq [hh:mm:ss:mss] Files (4)
118136

119137
Info seq [hh:mm:ss:mss] -----------------------------------------------
120138
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject2*' (Inferred)
121139
Info seq [hh:mm:ss:mss] Files (4)
122140

141+
Info seq [hh:mm:ss:mss] -----------------------------------------------
142+
Info seq [hh:mm:ss:mss] Project '/dev/null/autoImportProviderProject1*' (AutoImportProvider)
143+
Info seq [hh:mm:ss:mss] Files (2)
144+
123145
Info seq [hh:mm:ss:mss] -----------------------------------------------
124146
Info seq [hh:mm:ss:mss] Open files:
125147
Info seq [hh:mm:ss:mss] FileName: /project/node_modules/aws-sdk/package.json ProjectRootPath: undefined
@@ -134,6 +156,12 @@ watchedFiles::
134156
{"pollingInterval":500}
135157
/lib.decorators.legacy.d.ts:
136158
{"pollingInterval":500}
159+
/project/node_modules/aws-sdk/clients/s3.d.ts: *new*
160+
{"pollingInterval":500}
161+
/project/node_modules/aws-sdk/index.d.ts: *new*
162+
{"pollingInterval":500}
163+
/project/node_modules/aws-sdk/package.json: *new*
164+
{"pollingInterval":2000}
137165
/project/package.json: *new*
138166
{"pollingInterval":250}
139167

@@ -984,6 +1012,7 @@ Info seq [hh:mm:ss:mss] request:
9841012
},
9851013
"command": "getCodeFixes"
9861014
}
1015+
Info seq [hh:mm:ss:mss] forEachExternalModuleToImportFrom autoImportProvider: *
9871016
Info seq [hh:mm:ss:mss] response:
9881017
{
9891018
"seq": 0,

0 commit comments

Comments
 (0)