Skip to content

Commit 1020ea2

Browse files
committed
If there is no changes to folder structure when watching directories recursively, send the updates to fileNames only
Fixes #37994
1 parent e832e04 commit 1020ea2

4 files changed

Lines changed: 62 additions & 56 deletions

File tree

src/compiler/core.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2049,17 +2049,20 @@ namespace ts {
20492049
let oldIndex = 0;
20502050
const newLen = newItems.length;
20512051
const oldLen = oldItems.length;
2052+
let hasChanges = false;
20522053
while (newIndex < newLen && oldIndex < oldLen) {
20532054
const newItem = newItems[newIndex];
20542055
const oldItem = oldItems[oldIndex];
20552056
const compareResult = comparer(newItem, oldItem);
20562057
if (compareResult === Comparison.LessThan) {
20572058
inserted(newItem);
20582059
newIndex++;
2060+
hasChanges = true;
20592061
}
20602062
else if (compareResult === Comparison.GreaterThan) {
20612063
deleted(oldItem);
20622064
oldIndex++;
2065+
hasChanges = true;
20632066
}
20642067
else {
20652068
unchanged(oldItem, newItem);
@@ -2069,10 +2072,13 @@ namespace ts {
20692072
}
20702073
while (newIndex < newLen) {
20712074
inserted(newItems[newIndex++]);
2075+
hasChanges = true;
20722076
}
20732077
while (oldIndex < oldLen) {
20742078
deleted(oldItems[oldIndex++]);
2079+
hasChanges = true;
20752080
}
2081+
return hasChanges;
20762082
}
20772083

20782084
export function fill<T>(length: number, cb: (index: number) => T): T[] {

src/compiler/sys.ts

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ namespace ts {
475475

476476
const cache = createMap<HostDirectoryWatcher>();
477477
const callbackCache = createMultiMap<{ dirName: string; callback: DirectoryWatcherCallback; }>();
478-
const cacheToUpdateChildWatches = createMap<{ dirName: string; options: WatchOptions | undefined; }>();
478+
const cacheToUpdateChildWatches = createMap<{ dirName: string; options: WatchOptions | undefined; fileNames: string[]; }>();
479479
let timerToUpdateChildWatches: any;
480480

481481
const filePathComparer = getStringComparer(!host.useCaseSensitiveFileNames);
@@ -538,9 +538,12 @@ namespace ts {
538538
};
539539
}
540540

541-
function invokeCallbacks(dirPath: Path, fileNameOrInvokeMap: string | Map<true>) {
541+
type InvokeMap = Map<string[] | true>;
542+
function invokeCallbacks(dirPath: Path, fileName: string): void;
543+
function invokeCallbacks(dirPath: Path, invokeMap: InvokeMap, fileNames: string[] | undefined): void;
544+
function invokeCallbacks(dirPath: Path, fileNameOrInvokeMap: string | InvokeMap, fileNames?: string[]) {
542545
let fileName: string | undefined;
543-
let invokeMap: Map<true> | undefined;
546+
let invokeMap: InvokeMap | undefined;
544547
if (isString(fileNameOrInvokeMap)) {
545548
fileName = fileNameOrInvokeMap;
546549
}
@@ -549,10 +552,21 @@ namespace ts {
549552
}
550553
// Call the actual callback
551554
callbackCache.forEach((callbacks, rootDirName) => {
552-
if (invokeMap && invokeMap.has(rootDirName)) return;
555+
if (invokeMap && invokeMap.get(rootDirName) === true) return;
553556
if (rootDirName === dirPath || (startsWith(dirPath, rootDirName) && dirPath[rootDirName.length] === directorySeparator)) {
554557
if (invokeMap) {
555-
invokeMap.set(rootDirName, true);
558+
if (fileNames) {
559+
const existing = invokeMap.get(rootDirName);
560+
if (existing) {
561+
(existing as string[]).push(...fileNames);
562+
}
563+
else {
564+
invokeMap.set(rootDirName, fileNames.slice());
565+
}
566+
}
567+
else {
568+
invokeMap.set(rootDirName, true);
569+
}
556570
}
557571
else {
558572
callbacks.forEach(({ callback }) => callback(fileName!));
@@ -566,7 +580,7 @@ namespace ts {
566580
const parentWatcher = cache.get(dirPath);
567581
if (parentWatcher && host.directoryExists(dirName)) {
568582
// Schedule the update and postpone invoke for callbacks
569-
scheduleUpdateChildWatches(dirName, dirPath, options);
583+
scheduleUpdateChildWatches(dirName, dirPath, fileName, options);
570584
return;
571585
}
572586

@@ -575,9 +589,13 @@ namespace ts {
575589
removeChildWatches(parentWatcher);
576590
}
577591

578-
function scheduleUpdateChildWatches(dirName: string, dirPath: Path, options: WatchOptions | undefined) {
579-
if (!cacheToUpdateChildWatches.has(dirPath)) {
580-
cacheToUpdateChildWatches.set(dirPath, { dirName, options });
592+
function scheduleUpdateChildWatches(dirName: string, dirPath: Path, fileName: string, options: WatchOptions | undefined) {
593+
const existing = cacheToUpdateChildWatches.get(dirPath);
594+
if (existing) {
595+
existing.fileNames.push(fileName);
596+
}
597+
else {
598+
cacheToUpdateChildWatches.set(dirPath, { dirName, options, fileNames: [fileName] });
581599
}
582600
if (timerToUpdateChildWatches) {
583601
host.clearTimeout(timerToUpdateChildWatches);
@@ -590,22 +608,30 @@ namespace ts {
590608
timerToUpdateChildWatches = undefined;
591609
sysLog(`sysLog:: onTimerToUpdateChildWatches:: ${cacheToUpdateChildWatches.size}`);
592610
const start = timestamp();
593-
const invokeMap = createMap<true>();
611+
const invokeMap = createMap<string[]>();
594612

595613
while (!timerToUpdateChildWatches && cacheToUpdateChildWatches.size) {
596-
const { value: [dirPath, { dirName, options }], done } = cacheToUpdateChildWatches.entries().next();
614+
const { value: [dirPath, { dirName, options, fileNames }], done } = cacheToUpdateChildWatches.entries().next();
597615
Debug.assert(!done);
598616
cacheToUpdateChildWatches.delete(dirPath);
599617
// Because the child refresh is fresh, we would need to invalidate whole root directory being watched
600618
// to ensure that all the changes are reflected at this time
601-
invokeCallbacks(dirPath as Path, invokeMap);
602-
updateChildWatches(dirName, dirPath as Path, options);
619+
const hasChanges = updateChildWatches(dirName, dirPath as Path, options);
620+
invokeCallbacks(dirPath as Path, invokeMap, hasChanges ? undefined : fileNames);
603621
}
604622

605623
sysLog(`sysLog:: invokingWatchers:: ${timestamp() - start}ms:: ${cacheToUpdateChildWatches.size}`);
606624
callbackCache.forEach((callbacks, rootDirName) => {
607-
if (invokeMap.has(rootDirName)) {
608-
callbacks.forEach(({ callback, dirName }) => callback(dirName));
625+
const existing = invokeMap.get(rootDirName);
626+
if (existing) {
627+
callbacks.forEach(({ callback, dirName }) => {
628+
if (isArray(existing)) {
629+
existing.forEach(callback);
630+
}
631+
else {
632+
callback(dirName);
633+
}
634+
});
609635
}
610636
});
611637

@@ -623,34 +649,26 @@ namespace ts {
623649
}
624650
}
625651

626-
function updateChildWatches(dirName: string, dirPath: Path, options: WatchOptions | undefined) {
652+
function updateChildWatches(parentDir: string, parentDirPath: Path, options: WatchOptions | undefined) {
627653
// Iterate through existing children and update the watches if needed
628-
const parentWatcher = cache.get(dirPath);
629-
if (parentWatcher) {
630-
parentWatcher.childWatches = watchChildDirectories(dirName, parentWatcher.childWatches, options);
631-
}
632-
}
633-
634-
/**
635-
* Watch the directories in the parentDir
636-
*/
637-
function watchChildDirectories(parentDir: string, existingChildWatches: ChildWatches, options: WatchOptions | undefined): ChildWatches {
654+
const parentWatcher = cache.get(parentDirPath);
655+
if (!parentWatcher) return false;
638656
let newChildWatches: ChildDirectoryWatcher[] | undefined;
639-
enumerateInsertsAndDeletes<string, ChildDirectoryWatcher>(
657+
const hasChanges = enumerateInsertsAndDeletes<string, ChildDirectoryWatcher>(
640658
host.directoryExists(parentDir) ? mapDefined(host.getAccessibleSortedChildDirectories(parentDir), child => {
641659
const childFullName = getNormalizedAbsolutePath(child, parentDir);
642660
// Filter our the symbolic link directories since those arent included in recursive watch
643661
// which is same behaviour when recursive: true is passed to fs.watch
644662
return !isIgnoredPath(childFullName) && filePathComparer(childFullName, normalizePath(host.realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined;
645663
}) : emptyArray,
646-
existingChildWatches,
664+
parentWatcher.childWatches,
647665
(child, childWatcher) => filePathComparer(child, childWatcher.dirName),
648666
createAndAddChildDirectoryWatcher,
649667
closeFileWatcher,
650668
addChildDirectoryWatcher
651669
);
652-
653-
return newChildWatches || emptyArray;
670+
parentWatcher.childWatches = newChildWatches || emptyArray;
671+
return hasChanges;
654672

655673
/**
656674
* Create new childDirectoryWatcher and add it to the new ChildDirectoryWatcher list

src/testRunner/unittests/tscWatch/watchEnvironment.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,10 @@ namespace ts.tscWatch {
185185
},
186186
changes: [
187187
{
188-
caption: "Pending updates because of file1.js creation",
188+
caption: "Directory watch updates because of file1.js creation",
189189
change: noop,
190190
timeouts: sys => {
191191
sys.checkTimeoutQueueLengthAndRun(1); // To update directory callbacks for file1.js output
192-
sys.checkTimeoutQueueLengthAndRun(2); // Update program again and Failed lookup update
193-
sys.checkTimeoutQueueLengthAndRun(1); // Actual program update
194192
sys.checkTimeoutQueueLength(0);
195193
},
196194
},

tests/baselines/reference/tscWatch/watchEnvironment/watchDirectories/with-non-synchronous-watch-directory.js

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -76,27 +76,11 @@ exports.__esModule = true;
7676

7777

7878

79-
Change:: Pending updates because of file1.js creation
79+
Change:: Directory watch updates because of file1.js creation
8080

8181
Input::
8282

8383
Output::
84-
>> Screen clear
85-
[12:00:33 AM] File change detected. Starting incremental compilation...
86-
87-
88-
[12:00:34 AM] Found 0 errors. Watching for file changes.
89-
90-
91-
92-
Program root files: ["/user/username/projects/myproject/src/file1.ts"]
93-
Program options: {"watch":true,"project":"/user/username/projects/myproject/tsconfig.json","configFilePath":"/user/username/projects/myproject/tsconfig.json"}
94-
Program files::
95-
/a/lib/lib.d.ts
96-
/user/username/projects/myproject/node_modules/file2/index.d.ts
97-
/user/username/projects/myproject/src/file1.ts
98-
99-
Semantic diagnostics in builder refreshed for::
10084

10185
WatchedFiles::
10286
/user/username/projects/myproject/tsconfig.json:
@@ -132,7 +116,7 @@ Input::
132116

133117
Output::
134118
>> Screen clear
135-
[[90m12:00:38 AM[0m] File change detected. Starting incremental compilation...
119+
[[90m12:00:36 AM[0m] File change detected. Starting incremental compilation...
136120

137121

138122
user/username/projects/myproject/src/file1.ts:1:19 - error TS2307: Cannot find module 'file2' or its corresponding type declarations.
@@ -141,7 +125,7 @@ Output::
141125
   ~~~~~~~
142126

143127

144-
[[90m12:00:42 AM[0m] Found 1 error. Watching for file changes.
128+
[[90m12:00:40 AM[0m] Found 1 error. Watching for file changes.
145129

146130

147131

@@ -184,7 +168,7 @@ Input::
184168

185169
Output::
186170
>> Screen clear
187-
[[90m12:00:43 AM[0m] File change detected. Starting incremental compilation...
171+
[[90m12:00:41 AM[0m] File change detected. Starting incremental compilation...
188172

189173

190174
user/username/projects/myproject/src/file1.ts:1:19 - error TS2307: Cannot find module 'file2' or its corresponding type declarations.
@@ -193,7 +177,7 @@ Output::
193177
   ~~~~~~~
194178

195179

196-
[[90m12:00:44 AM[0m] Found 1 error. Watching for file changes.
180+
[[90m12:00:42 AM[0m] Found 1 error. Watching for file changes.
197181

198182

199183

@@ -386,10 +370,10 @@ Input::
386370

387371
Output::
388372
>> Screen clear
389-
[[90m12:00:51 AM[0m] File change detected. Starting incremental compilation...
373+
[[90m12:00:49 AM[0m] File change detected. Starting incremental compilation...
390374

391375

392-
[[90m12:00:55 AM[0m] Found 0 errors. Watching for file changes.
376+
[[90m12:00:53 AM[0m] Found 0 errors. Watching for file changes.
393377

394378

395379

0 commit comments

Comments
 (0)