Skip to content

Watch project directories that are close to the filesystem root - #64366

Open
soso (Generalsimus) wants to merge 1 commit into
microsoft:mainfrom
Generalsimus:fix-watch-shallow-project-dirs
Open

soso (Generalsimus) wants to merge 1 commit into
microsoft:mainfrom
Generalsimus:fix-watch-shallow-project-dirs

Conversation

@Generalsimus

Copy link
Copy Markdown

tsc --watch never rebuilds when the project lives close to the filesystem root, for example /app, /srv/app, /server (a common Docker WORKDIR) or /home/user/project. Nothing is printed. TypeScript 6.0 rebuilds in the same setup, so this is a 6.0/7.0 difference.

Reproduce

mkdir /app && cd /app        # any path with fewer than 5 components, e.g. /home/user/project
echo '{"compilerOptions":{"outDir":"out"},"include":["*.ts"]}' > tsconfig.json
echo 'export const x = "AAA";' > a.ts
TS_WATCH_DEBUG=1 tsc --watch  # then edit a.ts

TS_WATCH_DEBUG=1 shows why nothing happens:

[watch] no watchable ancestor for /app

Cause

WatchManager.ResolveDesiredDirs runs CanWatchDirectory on every directory it is given, including the ones the project itself declares: the wildcard directories from include, the tsconfig directory and the cwd. CanWatchDirectory requires at least 5 path components (/a/b/c/d), so a shallower directory is dropped without any fallback or diagnostic and no source file in it is ever watched. Only node_modules was still watched, which made it look like the watcher was running.

In TypeScript 6.0 the same heuristic (canWatchDirectoryOrFile, identical numbers) is only used by the module resolution cache for failed lookup locations. Program source files are watched individually at any depth. Here it also filters the project's own directories.

The rule is meant to stop the ancestor fallback (a missing directory resolving to /, /home, /home/user) from watching something far too generic. That is the only place it should apply.

Fix

In ResolveDesiredDirs, apply CanWatchDirectory only when the directory had to fall back to an ancestor. A directory that exists and was asked for is watched at any depth. Directories that tsc infers on its own (failed package.json lookups, bundled:///libs, ...) are still guarded, because they are not added through this path.

Verification

Same project, unfixed and fixed native tsc, edit a.ts once after the first build:

Project path Before After
/probe (Docker overlayfs) dir rejected, 0 rebuilds 1 rebuild, output updated
/srv/app (Docker overlayfs) dir rejected, 0 rebuilds 1 rebuild, output updated
/home/<user>/proj (host, 3 dirs deep) dir rejected, 0 rebuilds 1 rebuild, output updated

Also checked with a real project laid out as /server plus /shared (rootDir: "..", include: ["**/*.ts", "../shared/**/*.ts"]): before, no watchable ancestor for /server and /shared and no rebuild; after, both are watched and edits rebuild. TypeScript 6.0.3 rebuilds for the same shallow path.

Tests:

  • TestResolveDesiredDirsShallowProject fails without the change (all four shallow directories are dropped) and passes with it.
  • TestResolveDesiredDirsAncestorFallback pins the part that must stay: a missing directory never falls back to /app, /home/user or similar.
  • go test ./internal/execute/... passes.

Known limitation

Directories that are only inferred from files the program reads (for example a file imported from outside include, or a files-only tsconfig importing from a shallow subdirectory) still go through the depth check in computeDesiredWatches and the tsc -b orchestrator. I did not change that: the same list also holds failed lookups such as /home/<user>/package.json and the virtual bundled:///libs, and loosening the check there makes the watch setup fail on those. Telling real source files apart from lookups needs a separate, larger change. Happy to follow up if you want it.

AI disclosure

Written with the help of an AI coding assistant, as described in CONTRIBUTING.md. I ran into this in a Docker container and will follow up on review feedback.

ResolveDesiredDirs ran CanWatchDirectory on every desired directory,
including the ones the project itself declares (wildcard include
directories, the tsconfig directory, the cwd). CanWatchDirectory needs at
least five path components, so a project in /app, /srv/app or
/home/user/project was silently dropped and `tsc --watch` never rebuilt.

Only apply the check when falling back to an ancestor of a directory that
does not exist, which is what it is meant to guard against (/, /home, ...).
A directory that exists and was asked for is watched at any depth, like tsc
6.0 watches every program file.
Copilot AI balanced review requested due to automatic review settings September 20, 2026 22:26
@typescript-automation typescript-automation Bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Sep 20, 2026
@github-project-automation github-project-automation Bot moved this to Not started in PR Backlog Sep 20, 2026
@typescript-automation typescript-automation Bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Sep 20, 2026
@typescript-automation

Copy link
Copy Markdown

This PR doesn't have any linked issues. Please open an issue that references this PR. From there we can discuss and prioritise.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The focused logic change matches the intended behavior and includes appropriate regression coverage.

Review effort: Balanced
Findings: None

What changed in this PR

Fixes shallow-root project directories being excluded from tsc --watch.

Changes:

  • Applies directory-depth filtering only to ancestor fallbacks.
  • Adds tests for shallow projects and unsafe fallback ancestors.
File Description
tsc/​internal/​execute/​watchmanager/​watchmanager.go Allows explicitly requested existing directories at any depth.
tsc/​internal/​execute/​watchmanager/​watchmanager_test.go Covers shallow directories and ancestor safeguards.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

@Generalsimus

Copy link
Copy Markdown
Author
@microsoft-github-policy-service agree company="Microsoft"

@Generalsimus

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

Status: Not started

Development

Successfully merging this pull request may close these issues.

2 participants