Skip to content

Commit 1cd604a

Browse files
authored
Merge pull request microsoft#24933 from Microsoft/failedLookupWatchesWithClassicResolver
Do not watch parent folders recursively if not needed
2 parents 345012e + cd97adb commit 1cd604a

File tree

2 files changed

+82
-15
lines changed

2 files changed

+82
-15
lines changed

src/compiler/resolutionCache.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,14 @@ namespace ts {
6060
watcher: FileWatcher;
6161
/** ref count keeping this directory watch alive */
6262
refCount: number;
63+
/** is the directory watched being non recursive */
64+
nonRecursive?: boolean;
6365
}
6466

6567
interface DirectoryOfFailedLookupWatch {
6668
dir: string;
6769
dirPath: Path;
70+
nonRecursive?: boolean;
6871
ignore?: true;
6972
}
7073

@@ -251,7 +254,6 @@ namespace ts {
251254
perDirectoryResolution = createMap();
252255
perDirectoryCache.set(dirPath, perDirectoryResolution);
253256
}
254-
255257
const resolvedModules: R[] = [];
256258
const compilerOptions = resolutionHost.getCompilationSettings();
257259
const hasInvalidatedNonRelativeUnresolvedImport = logChanges && isFileWithInvalidatedNonRelativeUnresolvedImports(path);
@@ -393,6 +395,7 @@ namespace ts {
393395

394396
function getDirectoryToWatchFailedLookupLocation(failedLookupLocation: string, failedLookupLocationPath: Path): DirectoryOfFailedLookupWatch {
395397
if (isInDirectoryPath(rootPath, failedLookupLocationPath)) {
398+
// Always watch root directory recursively
396399
return { dir: rootDir!, dirPath: rootPath }; // TODO: GH#18217
397400
}
398401

@@ -409,11 +412,12 @@ namespace ts {
409412
dirPath = getDirectoryPath(dirPath);
410413
}
411414

412-
// If the directory is node_modules use it to watch
415+
// If the directory is node_modules use it to watch, always watch it recursively
413416
if (isNodeModulesDirectory(dirPath)) {
414417
return filterFSRootDirectoriesToWatch({ dir, dirPath }, getDirectoryPath(dirPath));
415418
}
416419

420+
let nonRecursive = true;
417421
// Use some ancestor of the root directory
418422
let subDirectoryPath: Path | undefined, subDirectory: string | undefined;
419423
if (rootPath !== undefined) {
@@ -422,14 +426,15 @@ namespace ts {
422426
if (parentPath === dirPath) {
423427
break;
424428
}
429+
nonRecursive = false;
425430
subDirectoryPath = dirPath;
426431
subDirectory = dir;
427432
dirPath = parentPath;
428433
dir = getDirectoryPath(dir);
429434
}
430435
}
431436

432-
return filterFSRootDirectoriesToWatch({ dir: subDirectory || dir, dirPath: subDirectoryPath || dirPath }, dirPath);
437+
return filterFSRootDirectoriesToWatch({ dir: subDirectory || dir, dirPath: subDirectoryPath || dirPath, nonRecursive }, dirPath);
433438
}
434439

435440
function isPathWithDefaultFailedLookupExtension(path: Path) {
@@ -452,7 +457,7 @@ namespace ts {
452457
let setAtRoot = false;
453458
for (const failedLookupLocation of failedLookupLocations) {
454459
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
455-
const { dir, dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
460+
const { dir, dirPath, nonRecursive, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
456461
if (!ignore) {
457462
// If the failed lookup location path is not one of the supported extensions,
458463
// store it in the custom path
@@ -464,23 +469,25 @@ namespace ts {
464469
setAtRoot = true;
465470
}
466471
else {
467-
setDirectoryWatcher(dir, dirPath);
472+
setDirectoryWatcher(dir, dirPath, nonRecursive);
468473
}
469474
}
470475
}
471476

472477
if (setAtRoot) {
478+
// This is always recursive
473479
setDirectoryWatcher(rootDir!, rootPath); // TODO: GH#18217
474480
}
475481
}
476482

477-
function setDirectoryWatcher(dir: string, dirPath: Path) {
483+
function setDirectoryWatcher(dir: string, dirPath: Path, nonRecursive?: boolean) {
478484
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
479485
if (dirWatcher) {
486+
Debug.assert(!!nonRecursive === !!dirWatcher.nonRecursive);
480487
dirWatcher.refCount++;
481488
}
482489
else {
483-
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 });
490+
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath, nonRecursive), refCount: 1, nonRecursive });
484491
}
485492
}
486493

@@ -530,7 +537,7 @@ namespace ts {
530537
dirWatcher.refCount--;
531538
}
532539

533-
function createDirectoryWatcher(directory: string, dirPath: Path) {
540+
function createDirectoryWatcher(directory: string, dirPath: Path, nonRecursive: boolean | undefined) {
534541
return resolutionHost.watchDirectoryOfFailedLookupLocation(directory, fileOrDirectory => {
535542
const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory);
536543
if (cachedDirectoryStructureHost) {
@@ -541,7 +548,7 @@ namespace ts {
541548
if (!allFilesHaveInvalidatedResolution && invalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath, dirPath === fileOrDirectoryPath)) {
542549
resolutionHost.onInvalidatedResolution();
543550
}
544-
}, WatchDirectoryFlags.Recursive);
551+
}, nonRecursive ? WatchDirectoryFlags.None : WatchDirectoryFlags.Recursive);
545552
}
546553

547554
function removeResolutionsOfFileFromCache(cache: Map<Map<ResolutionWithFailedLookupLocations>>, filePath: Path) {

src/testRunner/unittests/tsserverProjectSystem.ts

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8011,10 +8011,10 @@ new C();`
80118011
checkCompleteEvent(session, 2, expectedSequenceId);
80128012
}
80138013

8014-
function verifyWatchedFilesAndDirectories(host: TestServerHost, files: string[], directories: string[]) {
8014+
function verifyWatchedFilesAndDirectories(host: TestServerHost, files: string[], recursiveDirectories: string[], nonRecursiveDirectories: string[]) {
80158015
checkWatchedFilesDetailed(host, files.filter(f => f !== recognizersDateTimeSrcFile.path), 1);
8016-
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
8017-
checkWatchedDirectoriesDetailed(host, directories, 1, /*recursive*/ true);
8016+
checkWatchedDirectoriesDetailed(host, nonRecursiveDirectories, 1, /*recursive*/ false);
8017+
checkWatchedDirectoriesDetailed(host, recursiveDirectories, 1, /*recursive*/ true);
80188018
}
80198019

80208020
function createSessionAndOpenFile(host: TestServerHost) {
@@ -8035,22 +8035,23 @@ new C();`
80358035
const filesWithNodeModulesSetup = [...filesWithSources, nodeModulesRecorgnizersText];
80368036
const filesAfterCompilation = [...filesWithNodeModulesSetup, recongnizerTextDistTypingFile];
80378037

8038-
const watchedDirectoriesWithResolvedModule = [`${recognizersDateTime}/src`, withPathMapping ? packages : recognizersDateTime, ...getTypeRootsFromLocation(recognizersDateTime)];
8038+
const watchedDirectoriesWithResolvedModule = [`${recognizersDateTime}/src`, ...(withPathMapping ? emptyArray : [recognizersDateTime]), ...getTypeRootsFromLocation(recognizersDateTime)];
80398039
const watchedDirectoriesWithUnresolvedModule = [recognizersDateTime, ...(withPathMapping ? [recognizersText] : emptyArray), ...watchedDirectoriesWithResolvedModule, ...getNodeModuleDirectories(packages)];
8040+
const nonRecursiveWatchedDirectories = withPathMapping ? [packages] : emptyArray;
80408041

80418042
function verifyProjectWithResolvedModule(session: TestSession) {
80428043
const projectService = session.getProjectService();
80438044
const project = projectService.configuredProjects.get(recognizerDateTimeTsconfigPath)!;
80448045
checkProjectActualFiles(project, filesInProjectWithResolvedModule);
8045-
verifyWatchedFilesAndDirectories(session.host, filesInProjectWithResolvedModule, watchedDirectoriesWithResolvedModule);
8046+
verifyWatchedFilesAndDirectories(session.host, filesInProjectWithResolvedModule, watchedDirectoriesWithResolvedModule, nonRecursiveWatchedDirectories);
80468047
verifyErrors(session, []);
80478048
}
80488049

80498050
function verifyProjectWithUnresolvedModule(session: TestSession) {
80508051
const projectService = session.getProjectService();
80518052
const project = projectService.configuredProjects.get(recognizerDateTimeTsconfigPath)!;
80528053
checkProjectActualFiles(project, filesInProjectWithUnresolvedModule);
8053-
verifyWatchedFilesAndDirectories(session.host, filesInProjectWithUnresolvedModule, watchedDirectoriesWithUnresolvedModule);
8054+
verifyWatchedFilesAndDirectories(session.host, filesInProjectWithUnresolvedModule, watchedDirectoriesWithUnresolvedModule, nonRecursiveWatchedDirectories);
80548055
const startOffset = recognizersDateTimeSrcFile.content.indexOf('"') + 1;
80558056
verifyErrors(session, [
80568057
createDiagnostic({ line: 1, offset: startOffset }, { line: 1, offset: startOffset + moduleNameInFile.length }, Diagnostics.Cannot_find_module_0, [moduleName])
@@ -8506,6 +8507,65 @@ new C();`
85068507
}
85078508
});
85088509
});
8510+
8511+
it("when watching directories for failed lookup locations in amd resolution", () => {
8512+
const projectRoot = "/user/username/projects/project";
8513+
const nodeFile: File = {
8514+
path: `${projectRoot}/src/typings/node.d.ts`,
8515+
content: `
8516+
declare module "fs" {
8517+
export interface something {
8518+
}
8519+
}`
8520+
};
8521+
const electronFile: File = {
8522+
path: `${projectRoot}/src/typings/electron.d.ts`,
8523+
content: `
8524+
declare module 'original-fs' {
8525+
import * as fs from 'fs';
8526+
export = fs;
8527+
}`
8528+
};
8529+
const srcFile: File = {
8530+
path: `${projectRoot}/src/somefolder/srcfile.ts`,
8531+
content: `
8532+
import { x } from "somefolder/module1";
8533+
import { x } from "somefolder/module2";
8534+
const y = x;`
8535+
};
8536+
const moduleFile: File = {
8537+
path: `${projectRoot}/src/somefolder/module1.ts`,
8538+
content: `
8539+
export const x = 10;`
8540+
};
8541+
const configFile: File = {
8542+
path: `${projectRoot}/src/tsconfig.json`,
8543+
content: JSON.stringify({
8544+
compilerOptions: {
8545+
module: "amd",
8546+
moduleResolution: "classic",
8547+
target: "es5",
8548+
outDir: "../out",
8549+
baseUrl: "./",
8550+
typeRoots: ["typings"]
8551+
8552+
}
8553+
})
8554+
};
8555+
const files = [nodeFile, electronFile, srcFile, moduleFile, configFile, libFile];
8556+
const host = createServerHost(files);
8557+
const service = createProjectService(host);
8558+
service.openClientFile(srcFile.path, srcFile.content, ScriptKind.TS, projectRoot);
8559+
checkProjectActualFiles(service.configuredProjects.get(configFile.path)!, files.map(f => f.path));
8560+
checkWatchedFilesDetailed(host, mapDefined(files, f => f === srcFile ? undefined : f.path), 1);
8561+
checkWatchedDirectoriesDetailed(host, [`${projectRoot}`], 1, /*recursive*/ false); // failed lookup for fs
8562+
const expectedWatchedDirectories = createMap<number>();
8563+
expectedWatchedDirectories.set(`${projectRoot}/src`, 2); // Wild card and failed lookup
8564+
expectedWatchedDirectories.set(`${projectRoot}/somefolder`, 1); // failed lookup for somefolder/module2
8565+
expectedWatchedDirectories.set(`${projectRoot}/node_modules`, 1); // failed lookup for with node_modules/@types/fs
8566+
expectedWatchedDirectories.set(`${projectRoot}/src/typings`, 1); // typeroot directory
8567+
checkWatchedDirectoriesDetailed(host, expectedWatchedDirectories, /*recursive*/ true);
8568+
});
85098569
});
85108570

85118571
describe("tsserverProjectSystem watchDirectories implementation", () => {

0 commit comments

Comments
 (0)