Skip to content

Commit 1a2fda0

Browse files
committed
Add support to update module resolutions on watches from mono repo like setup with path mapping
Fixes #22349
1 parent 528bf84 commit 1a2fda0

File tree

2 files changed

+54
-12
lines changed

2 files changed

+54
-12
lines changed

src/compiler/resolutionCache.ts

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,15 @@ namespace ts {
5959
watcher: FileWatcher;
6060
/** ref count keeping this directory watch alive */
6161
refCount: number;
62+
/** map of refcount for the subDirectory */
63+
subDirectoryMap?: Map<number>;
6264
}
6365

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

7073
export const maxNumberOfFilesToIterateForInvalidation = 256;
@@ -393,18 +396,20 @@ namespace ts {
393396
}
394397

395398
// Use some ancestor of the root directory
399+
let subDirectory: Path | undefined;
396400
if (rootPath !== undefined) {
397401
while (!isInDirectoryPath(dirPath, rootPath)) {
398402
const parentPath = getDirectoryPath(dirPath);
399403
if (parentPath === dirPath) {
400404
break;
401405
}
406+
subDirectory = dirPath.slice(parentPath.length + directorySeparator.length) as Path;
402407
dirPath = parentPath;
403408
dir = getDirectoryPath(dir);
404409
}
405410
}
406411

407-
return filterFSRootDirectoriesToWatch({ dir, dirPath }, dirPath);
412+
return filterFSRootDirectoriesToWatch({ dir, dirPath, subDirectory }, dirPath);
408413
}
409414

410415
function isPathWithDefaultFailedLookupExtension(path: Path) {
@@ -427,7 +432,7 @@ namespace ts {
427432
let setAtRoot = false;
428433
for (const failedLookupLocation of failedLookupLocations) {
429434
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
430-
const { dir, dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
435+
const { dir, dirPath, ignore , subDirectory } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
431436
if (!ignore) {
432437
// If the failed lookup location path is not one of the supported extensions,
433438
// store it in the custom path
@@ -439,7 +444,7 @@ namespace ts {
439444
setAtRoot = true;
440445
}
441446
else {
442-
setDirectoryWatcher(dir, dirPath);
447+
setDirectoryWatcher(dir, dirPath, subDirectory);
443448
}
444449
}
445450
}
@@ -449,13 +454,20 @@ namespace ts {
449454
}
450455
}
451456

452-
function setDirectoryWatcher(dir: string, dirPath: Path) {
453-
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
457+
function setDirectoryWatcher(dir: string, dirPath: Path, subDirectory?: Path) {
458+
let dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
454459
if (dirWatcher) {
455460
dirWatcher.refCount++;
456461
}
457462
else {
458-
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 });
463+
dirWatcher = { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 };
464+
directoryWatchesOfFailedLookups.set(dirPath, dirWatcher);
465+
}
466+
467+
if (subDirectory) {
468+
const subDirectoryMap = dirWatcher.subDirectoryMap || (dirWatcher.subDirectoryMap = createMap());
469+
const existing = subDirectoryMap.get(subDirectory) || 0;
470+
subDirectoryMap.set(subDirectory, existing + 1);
459471
}
460472
}
461473

@@ -473,7 +485,7 @@ namespace ts {
473485
let removeAtRoot = false;
474486
for (const failedLookupLocation of failedLookupLocations) {
475487
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
476-
const { dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
488+
const { dirPath, ignore, subDirectory } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
477489
if (!ignore) {
478490
const refCount = customFailedLookupPaths.get(failedLookupLocationPath);
479491
if (refCount) {
@@ -490,7 +502,7 @@ namespace ts {
490502
removeAtRoot = true;
491503
}
492504
else {
493-
removeDirectoryWatcher(dirPath);
505+
removeDirectoryWatcher(dirPath, subDirectory);
494506
}
495507
}
496508
}
@@ -499,12 +511,30 @@ namespace ts {
499511
}
500512
}
501513

502-
function removeDirectoryWatcher(dirPath: string) {
514+
function removeDirectoryWatcher(dirPath: string, subDirectory?: Path) {
503515
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
516+
if (subDirectory) {
517+
const existing = dirWatcher.subDirectoryMap.get(subDirectory);
518+
if (existing === 1) {
519+
dirWatcher.subDirectoryMap.delete(subDirectory);
520+
}
521+
else {
522+
dirWatcher.subDirectoryMap.set(subDirectory, existing - 1);
523+
}
524+
}
504525
// Do not close the watcher yet since it might be needed by other failed lookup locations.
505526
dirWatcher.refCount--;
506527
}
507528

529+
function inWatchedSubdirectory(dirPath: Path, fileOrDirectoryPath: Path) {
530+
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
531+
if (!dirWatcher || !dirWatcher.subDirectoryMap) return false;
532+
return forEachKey(dirWatcher.subDirectoryMap, subDirectory => {
533+
const fullSubDirectory = `${dirPath}/${subDirectory}` as Path;
534+
return fullSubDirectory === fileOrDirectoryPath || isInDirectoryPath(fullSubDirectory, fileOrDirectoryPath);
535+
});
536+
}
537+
508538
function createDirectoryWatcher(directory: string, dirPath: Path) {
509539
return resolutionHost.watchDirectoryOfFailedLookupLocation(directory, fileOrDirectory => {
510540
const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory);
@@ -516,7 +546,7 @@ namespace ts {
516546
// If the files are added to project root or node_modules directory, always run through the invalidation process
517547
// Otherwise run through invalidation only if adding to the immediate directory
518548
if (!allFilesHaveInvalidatedResolution &&
519-
dirPath === rootPath || isNodeModulesDirectory(dirPath) || getDirectoryPath(fileOrDirectoryPath) === dirPath) {
549+
(dirPath === rootPath || isNodeModulesDirectory(dirPath) || getDirectoryPath(fileOrDirectoryPath) === dirPath || inWatchedSubdirectory(dirPath, fileOrDirectoryPath))) {
520550
if (invalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath, dirPath === fileOrDirectoryPath)) {
521551
resolutionHost.onInvalidatedResolution();
522552
}

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7861,7 +7861,13 @@ new C();`
78617861
host.reloadFS(filesAfterCompilation);
78627862
host.runQueuedTimeoutCallbacks();
78637863

7864-
verifyProjectWithResolvedModule(session);
7864+
if (withPathMapping) {
7865+
verifyProjectWithResolvedModule(session);
7866+
}
7867+
else {
7868+
// Cannot handle the resolution update
7869+
verifyProjectWithUnresolvedModule(session);
7870+
}
78657871
});
78667872

78677873
it("when project recompiles after deleting generated folders", () => {
@@ -7878,7 +7884,13 @@ new C();`
78787884
host.ensureFileOrFolder(recongnizerTextDistTypingFile);
78797885
host.runQueuedTimeoutCallbacks();
78807886

7881-
verifyProjectWithResolvedModule(session);
7887+
if (withPathMapping) {
7888+
verifyProjectWithResolvedModule(session);
7889+
}
7890+
else {
7891+
// Cannot handle the resolution update
7892+
verifyProjectWithUnresolvedModule(session);
7893+
}
78827894
});
78837895
});
78847896
}

0 commit comments

Comments
 (0)