Skip to content

Commit

Permalink
Add support to update module resolutions on watches from mono repo li…
Browse files Browse the repository at this point in the history
…ke setup with path mapping

Fixes #22349
  • Loading branch information
sheetalkamat committed May 4, 2018
1 parent 528bf84 commit 1a2fda0
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 12 deletions.
50 changes: 40 additions & 10 deletions src/compiler/resolutionCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,15 @@ namespace ts {
watcher: FileWatcher;
/** ref count keeping this directory watch alive */
refCount: number;
/** map of refcount for the subDirectory */
subDirectoryMap?: Map<number>;
}

interface DirectoryOfFailedLookupWatch {
dir: string;
dirPath: Path;
ignore?: true;
subDirectory?: Path;
}

export const maxNumberOfFilesToIterateForInvalidation = 256;
Expand Down Expand Up @@ -393,18 +396,20 @@ namespace ts {
}

// Use some ancestor of the root directory
let subDirectory: Path | undefined;
if (rootPath !== undefined) {
while (!isInDirectoryPath(dirPath, rootPath)) {
const parentPath = getDirectoryPath(dirPath);
if (parentPath === dirPath) {
break;
}
subDirectory = dirPath.slice(parentPath.length + directorySeparator.length) as Path;
dirPath = parentPath;
dir = getDirectoryPath(dir);
}
}

return filterFSRootDirectoriesToWatch({ dir, dirPath }, dirPath);
return filterFSRootDirectoriesToWatch({ dir, dirPath, subDirectory }, dirPath);
}

function isPathWithDefaultFailedLookupExtension(path: Path) {
Expand All @@ -427,7 +432,7 @@ namespace ts {
let setAtRoot = false;
for (const failedLookupLocation of failedLookupLocations) {
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
const { dir, dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
const { dir, dirPath, ignore , subDirectory } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
if (!ignore) {
// If the failed lookup location path is not one of the supported extensions,
// store it in the custom path
Expand All @@ -439,7 +444,7 @@ namespace ts {
setAtRoot = true;
}
else {
setDirectoryWatcher(dir, dirPath);
setDirectoryWatcher(dir, dirPath, subDirectory);
}
}
}
Expand All @@ -449,13 +454,20 @@ namespace ts {
}
}

function setDirectoryWatcher(dir: string, dirPath: Path) {
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
function setDirectoryWatcher(dir: string, dirPath: Path, subDirectory?: Path) {
let dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
if (dirWatcher) {
dirWatcher.refCount++;
}
else {
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 });
dirWatcher = { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 };
directoryWatchesOfFailedLookups.set(dirPath, dirWatcher);
}

if (subDirectory) {
const subDirectoryMap = dirWatcher.subDirectoryMap || (dirWatcher.subDirectoryMap = createMap());
const existing = subDirectoryMap.get(subDirectory) || 0;
subDirectoryMap.set(subDirectory, existing + 1);
}
}

Expand All @@ -473,7 +485,7 @@ namespace ts {
let removeAtRoot = false;
for (const failedLookupLocation of failedLookupLocations) {
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
const { dirPath, ignore } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
const { dirPath, ignore, subDirectory } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
if (!ignore) {
const refCount = customFailedLookupPaths.get(failedLookupLocationPath);
if (refCount) {
Expand All @@ -490,7 +502,7 @@ namespace ts {
removeAtRoot = true;
}
else {
removeDirectoryWatcher(dirPath);
removeDirectoryWatcher(dirPath, subDirectory);
}
}
}
Expand All @@ -499,12 +511,30 @@ namespace ts {
}
}

function removeDirectoryWatcher(dirPath: string) {
function removeDirectoryWatcher(dirPath: string, subDirectory?: Path) {
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
if (subDirectory) {
const existing = dirWatcher.subDirectoryMap.get(subDirectory);
if (existing === 1) {
dirWatcher.subDirectoryMap.delete(subDirectory);
}
else {
dirWatcher.subDirectoryMap.set(subDirectory, existing - 1);
}
}
// Do not close the watcher yet since it might be needed by other failed lookup locations.
dirWatcher.refCount--;
}

function inWatchedSubdirectory(dirPath: Path, fileOrDirectoryPath: Path) {
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
if (!dirWatcher || !dirWatcher.subDirectoryMap) return false;
return forEachKey(dirWatcher.subDirectoryMap, subDirectory => {
const fullSubDirectory = `${dirPath}/${subDirectory}` as Path;
return fullSubDirectory === fileOrDirectoryPath || isInDirectoryPath(fullSubDirectory, fileOrDirectoryPath);
});
}

function createDirectoryWatcher(directory: string, dirPath: Path) {
return resolutionHost.watchDirectoryOfFailedLookupLocation(directory, fileOrDirectory => {
const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory);
Expand All @@ -516,7 +546,7 @@ namespace ts {
// If the files are added to project root or node_modules directory, always run through the invalidation process
// Otherwise run through invalidation only if adding to the immediate directory
if (!allFilesHaveInvalidatedResolution &&
dirPath === rootPath || isNodeModulesDirectory(dirPath) || getDirectoryPath(fileOrDirectoryPath) === dirPath) {
(dirPath === rootPath || isNodeModulesDirectory(dirPath) || getDirectoryPath(fileOrDirectoryPath) === dirPath || inWatchedSubdirectory(dirPath, fileOrDirectoryPath))) {
if (invalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath, dirPath === fileOrDirectoryPath)) {
resolutionHost.onInvalidatedResolution();
}
Expand Down
16 changes: 14 additions & 2 deletions src/harness/unittests/tsserverProjectSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7861,7 +7861,13 @@ new C();`
host.reloadFS(filesAfterCompilation);
host.runQueuedTimeoutCallbacks();

verifyProjectWithResolvedModule(session);
if (withPathMapping) {
verifyProjectWithResolvedModule(session);
}
else {
// Cannot handle the resolution update
verifyProjectWithUnresolvedModule(session);
}
});

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

verifyProjectWithResolvedModule(session);
if (withPathMapping) {
verifyProjectWithResolvedModule(session);
}
else {
// Cannot handle the resolution update
verifyProjectWithUnresolvedModule(session);
}
});
});
}
Expand Down

0 comments on commit 1a2fda0

Please sign in to comment.