Check autoImportExcludePatterns against symlink paths (#57033)

This commit is contained in:
Andrew Branch
2024-01-16 09:35:26 -08:00
committed by GitHub
parent cf33fd0cde
commit a673958ccc
7 changed files with 2288 additions and 8 deletions

View File

@@ -8,6 +8,7 @@ import {
createMultiMap,
Debug,
emptyArray,
ensureTrailingDirectorySeparator,
findIndex,
firstDefined,
forEachAncestorDirectory,
@@ -43,6 +44,7 @@ import {
nodeModulesPathPart,
PackageJsonImportFilter,
Path,
pathContainsNodeModules,
Program,
skipAlias,
skipOuterExpressions,
@@ -427,12 +429,12 @@ export function forEachExternalModuleToImportFrom(
return pattern ? getRegexFromPattern(pattern, useCaseSensitiveFileNames) : undefined;
});
forEachExternalModule(program.getTypeChecker(), program.getSourceFiles(), excludePatterns, (module, file) => cb(module, file, program, /*isFromPackageJson*/ false));
forEachExternalModule(program.getTypeChecker(), program.getSourceFiles(), excludePatterns, host, (module, file) => cb(module, file, program, /*isFromPackageJson*/ false));
const autoImportProvider = useAutoImportProvider && host.getPackageJsonAutoImportProvider?.();
if (autoImportProvider) {
const start = timestamp();
const checker = program.getTypeChecker();
forEachExternalModule(autoImportProvider.getTypeChecker(), autoImportProvider.getSourceFiles(), excludePatterns, (module, file) => {
forEachExternalModule(autoImportProvider.getTypeChecker(), autoImportProvider.getSourceFiles(), excludePatterns, host, (module, file) => {
if (file && !program.getSourceFile(file.fileName) || !file && !checker.resolveName(module.name, /*location*/ undefined, SymbolFlags.Module, /*excludeGlobals*/ false)) {
// The AutoImportProvider filters files already in the main program out of its *root* files,
// 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(
}
}
function forEachExternalModule(checker: TypeChecker, allSourceFiles: readonly SourceFile[], excludePatterns: readonly RegExp[] | undefined, cb: (module: Symbol, sourceFile: SourceFile | undefined) => void) {
const isExcluded = excludePatterns && ((fileName: string) => excludePatterns.some(p => p.test(fileName)));
function forEachExternalModule(checker: TypeChecker, allSourceFiles: readonly SourceFile[], excludePatterns: readonly RegExp[] | undefined, host: LanguageServiceHost, cb: (module: Symbol, sourceFile: SourceFile | undefined) => void) {
const realpathsWithSymlinks = host.getSymlinkCache?.().getSymlinkedDirectoriesByRealpath();
const isExcluded = excludePatterns && (({ fileName, path }: SourceFile) => {
if (excludePatterns.some(p => p.test(fileName))) return true;
if (realpathsWithSymlinks?.size && pathContainsNodeModules(fileName)) {
let dir = getDirectoryPath(fileName);
return forEachAncestorDirectory(getDirectoryPath(path), dirPath => {
const symlinks = realpathsWithSymlinks.get(ensureTrailingDirectorySeparator(dirPath));
if (symlinks) {
return symlinks.some(s => excludePatterns.some(p => p.test(fileName.replace(dir, s))));
}
dir = getDirectoryPath(dir);
}) ?? false;
}
return false;
});
for (const ambient of checker.getAmbientModules()) {
if (!ambient.name.includes("*") && !(excludePatterns && ambient.declarations?.every(d => isExcluded!(d.getSourceFile().fileName)))) {
if (!ambient.name.includes("*") && !(excludePatterns && ambient.declarations?.every(d => isExcluded!(d.getSourceFile())))) {
cb(ambient, /*sourceFile*/ undefined);
}
}
for (const sourceFile of allSourceFiles) {
if (isExternalOrCommonJsModule(sourceFile) && !isExcluded?.(sourceFile.fileName)) {
if (isExternalOrCommonJsModule(sourceFile) && !isExcluded?.(sourceFile)) {
cb(checker.getMergedSymbol(sourceFile.symbol), sourceFile);
}
}