🔎 Search Terms
"tsc --watch symlink", "watch mode symlinked directory not detected", "walkDir symlink", "7.0 watch symlink", "fswatch symlink"
🕗 Version & Regression Information
⏯ Playground Link
Not applicable (watch mode / file system behaviour). Standalone repro below, no external dependencies.
💻 Code
Layout: a shared package whose src directory is symlinked into the app's src tree, a common pattern for shared code in monorepos.
repro/
pkg-common/src/types.ts
app/tsconfig.json
app/src/index.ts
app/src/common -> ../../pkg-common/src (symlink)
// pkg-common/src/types.ts
export type Greeting = { message: string };
// app/src/index.ts
import { Greeting } from './common/types';
const g: Greeting = { message: 'hi' };
console.log(g.message);
// app/tsconfig.json
{
"compilerOptions": { "target": "es2022", "module": "nodenext", "strict": true, "outDir": "dist", "types": [] },
"include": ["src/**/*.ts"]
}
Steps:
mkdir -p repro/pkg-common/src repro/app/src && cd repro
# create the three files above, then:
ln -s ../../pkg-common/src app/src/common
cd app && npx -p [email protected] tsc --watch --pretty false -p tsconfig.json
# in another terminal, each of these should trigger "File change detected":
echo '// edit' >> ../pkg-common/src/types.ts # real path -> nothing happens
echo '// edit' >> src/common/types.ts # via symlink -> nothing happens
echo '// edit' >> src/index.ts # regular file -> recompiles
🙁 Actual behavior
With TypeScript 7.0.2 (and @next), tsc --watch compiles the symlinked file on the initial build but never notices later edits to it, whether the edit is made through the real path or through the symlink path. Nothing is printed and no recompilation happens; introducing a type error in types.ts is not reported until the watcher is restarted. Edits to src/index.ts (not behind a symlink) are detected within a second.
Results on macOS 26.6.2 (arm64), node 22.22.0, 20 s wait per edit:
| Edit |
tsc 6.0.3 |
tsc 7.0.2 |
7.1.0-dev.20260925.1 |
pkg-common/src/types.ts (real path) |
detected |
not detected |
not detected |
app/src/common/types.ts (via symlink) |
detected |
not detected |
not detected |
app/src/index.ts (regular file) |
detected |
detected |
detected |
In a real project with three shared packages symlinked into src/ (about 1,800 files behind symlinks), this means every edit in those packages is silently ignored by the watcher, so tsc --watch cannot be used as the dev loop there.
🙂 Expected behavior
Edits to files that are part of the program should trigger a recompilation regardless of whether the file is reached through a symlinked directory, as in 6.0.3. If following symlinks during the directory walk is intentionally avoided, the watcher could still subscribe to the real directory of each program file that resolves through a symlink (6.x watched realpaths for this reason).
Additional information about the issue
- Only macOS was tested.
preserveSymlinks is not set (default false), so the program already knows the realpath of these files.
- Happy to test a fix or provide more details.
🔎 Search Terms
"tsc --watch symlink", "watch mode symlinked directory not detected", "walkDir symlink", "7.0 watch symlink", "fswatch symlink"
🕗 Version & Regression Information
typescript@next7.1.0-dev.20260925.1 and on the last@typescript/native-previewbuild, 7.0.0-dev.20260707.2).walkDirfrom following symlinks, including symlinked roots"), which resolves symlinks only for the watch root. 7.0.1-rc binary in watch mode fails to resolve symlinks in node_modules typescript-go#4372 (closed by that PR) covers symlinks insidenode_modules; this report is about a symlinked directory inside the project's own source tree.⏯ Playground Link
Not applicable (watch mode / file system behaviour). Standalone repro below, no external dependencies.
💻 Code
Layout: a shared package whose
srcdirectory is symlinked into the app'ssrctree, a common pattern for shared code in monorepos.Steps:
🙁 Actual behavior
With TypeScript 7.0.2 (and
@next),tsc --watchcompiles the symlinked file on the initial build but never notices later edits to it, whether the edit is made through the real path or through the symlink path. Nothing is printed and no recompilation happens; introducing a type error intypes.tsis not reported until the watcher is restarted. Edits tosrc/index.ts(not behind a symlink) are detected within a second.Results on macOS 26.6.2 (arm64), node 22.22.0, 20 s wait per edit:
pkg-common/src/types.ts(real path)app/src/common/types.ts(via symlink)app/src/index.ts(regular file)In a real project with three shared packages symlinked into
src/(about 1,800 files behind symlinks), this means every edit in those packages is silently ignored by the watcher, sotsc --watchcannot be used as the dev loop there.🙂 Expected behavior
Edits to files that are part of the program should trigger a recompilation regardless of whether the file is reached through a symlinked directory, as in 6.0.3. If following symlinks during the directory walk is intentionally avoided, the watcher could still subscribe to the real directory of each program file that resolves through a symlink (6.x watched realpaths for this reason).
Additional information about the issue
preserveSymlinksis not set (default false), so the program already knows the realpath of these files.