New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Watcher recursively watches irrelevant directories #58319
Comments
Looking further, this seems to be triggered by TypeScript unexpectedly trying to resolve an import of |
Issue updated with minimal repro. |
This is intentional as otherwise many watchers are created. for different resolutions and we want to avoid that. The watcher is smart to use lot of stuff so even if it triggers rebuild it will not do anything major. |
@dmichon-msft do you have a reproduction of the infinite build loop you mentioned on Teams? |
The infinite build loop is a consequence of that the orchestrator I use plugs into the CompilerHost's If there's a proper way for me to wire heft-typescript-plugin into TypeScript's watch compiler such that I can only intercept when it would actually trigger a rebuild, I'd be happy to adopt that instead and that would also provide a way to resolve. |
This is definitely not safe to do (as you've found out) If you're going to go into internals anyway... you could intercept |
You will notice that we use setTimeout to postpone determination of whether program needs to be rebuilt so definitely something you shouldnt be hacking into. You may also be able to override |
The reason I hook This works cleanly in webpack, because the API contract for file watching always specifies immediate parent directories, even though the backend is implemented with one or more recursive file watchers. I would argue that the decision to implement a watch via a recursive watcher should be an implementation detail of the WatchHost, not something that happens on the calling side. As it stands, even if I override |
This isn't an implementation detail; instead you're describing a different level of abstraction. At the level of abstraction that it actually handles, the implemented object can and does have a finite number of watch handles, e.g. trying to put individual file watches on several thousand folders in |
I don't think we are disagreeing? Suppose we have a folder
TypeScript observes that it probed for non-existent files in Option 1 - TypeScript asks for watch of exact foldersIn this path, TypeScript asks the watch host to watch for children in exactly Linux / other platform without recursive watch supportLinux has no native recursive watch support, so it has to end up watching every relevant directory directly regardless. This means it allocates the following non-recursive directory watchers:
Windows / OSX / other platform with recursive watch supportThe watch host is free to determine that allocating directory watchers for each of:
is too many handles, and consolidate on the common ancestor of Option 2 - TypeScript asks to recursively watch the ancestorIn this path TypeScript directly asks for the watch host to watch Linux / other platform without recursive watch supportThe OS doesn't know which parts of
This results in 3 more system file watchers than in option (1), as well as notifying TypeScript of irrelevant file changes and forcing it to calculate whether or not the change affects results. Windows / OSX / other platform with recursive watch supportThe OS allocates a recursive watcher for SummaryIn option (1), we use fewer system resources on platforms without native recursive watch support, and if there is recursive watch support, preliminary event filtering is handled in a self-contained component, rather than being the responsibility of the TypeScript compiler. In option (2), custom watch providers have no way of identifying which changes are actually of concern to the TypeScript compiler and can't provide any preliminary filtering. |
using Watching just needed folder can still cause this since you could still add some irrelevant file there and we need to handle that. |
My issue is that, when running under Linux, in TypeScript Following the steps in the repro, when using TypeScript 5.0.6, the complete log:
When running with Edit: After letting it run to see if it would finish, eventually the OS killed the process:
|
This was specifically handled to watch for mono repo scenarios correctly as part of #53591 , #55738 and some other changes . In 5.0 not watching We allow passing |
This issue has been marked as "Working as Intended" and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
🔎 Search Terms
watch, excess, files, directories
🕗 Version & Regression Information
⏯ Playground Link
No response
💻 Code
Repro
Steps:
Once the build finishes, create a file
tmp.txt
ina/2/logs
and observe that TypeScript acts on the file change.🙁 Actual behavior
If a folder is added to TypeScript's list of "failed lookup locations", any file change at any level within said folder will trigger a rebuild, even if the change is in a nested folder that was not probed during module resolution.
🙂 Expected behavior
TypeScript only watches contents of directories that contain files that were referenced in a tsconfig or probed during module resolution.
Additional information about the issue
Environment: GitHub Codespaces
OS: #19~22.04.1-Ubuntu SMP Wed Jan 10 22:57:03 UTC 2024
node 18.20.2
The text was updated successfully, but these errors were encountered: