microsoft / microsoft/TypeScript
Project references resolve a symlinked sibling's raw source instead of its own composite output, using the wrong project's compilerOptions
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Go
- Sterne
- 111k
- Forks
- 14.3k
- Ø Merge
- 2 T. 4 Std.
- Gemergte PRs (30 T.)
- 132
Beschreibung
Acknowledgement
- #63744
Comment
Project references resolve a symlinked sibling's raw source instead of its own composite output, using the wrong project's compilerOptions
Repo: Microsoft/TypeScript
TypeScript version: 6.0.3 (via Bun 1.3.14)
OS: Debian GNU/Linux 13 (trixie)
Kernel: Linux 6.12.96+deb13-amd64
Arch: x86_64
Summary
In a --build (tsc -b) solution with project references, when Project A's compiled .d.ts output re-exports a type from Project B, and Project B is reachable from node_modules only via a symlink, TypeScript resolves that re-exported type by reading Project B's raw .ts source under a foreign project's compilerOptions, not Project B's own. This causes real, valid source files to fail type-checking with missing ambient globals (Buffer, process, Bun, fs, dns, setImmediate) that are correctly declared via Project B's own "types" field.
Confirmed not package-manager-specific
Filed a companion report against Bun (oven-sh/bun) first, since symlinked node_modules/dann-sdk was the entry point observed. A Bun maintainer confirmed:
Workspace members are always linked as symlinks, under both linkers. Hoisted:
node_modules/pkg-a -> ../packages/a. Isolated:packages/b/node_modules/pkg-a -> ../../a. This is intentional and matches npm, yarn, and pnpm workspace behavior.
npm, yarn, and pnpm all symlink workspace-local sibling packages by default, the same as Bun. This means any project using TypeScript project references inside an npm, yarn, pnpm, or Bun workspace, where one package's compiled output re-exports a type from a sibling package, is a candidate for this bug. It is not a Bun quirk, it is a general symlink plus project-references interaction in TypeScript itself.
Repro
Minimal shape:
packages/
sdk/
tsconfig.json # "types": ["bun"], "composite": true
src/utils/settings-crypto.ts # uses Buffer, crypto (from @types/bun)
trace/
tsconfig.json # "composite": true, "outDir": "./dist"
kernel-log.ts # re-exports a type from sdk: `import type { LogCtx } from 'dann-sdk'`
leaf/
tsconfig.json # references: [{ "path": "../trace" }]
# leaf's own source never imports sdk directly
Root package.json:
{
"workspaces": ["packages/*"]
}
Steps:
- Install with any workspace-aware package manager (npm, yarn, pnpm, or Bun). Local packages get symlinked into
node_modules. tsc -b packages/trace/tsconfig.json, succeeds, emitspackages/trace/dist/kernel-log.d.tscontainingimport("dann-sdk").LogCtx.tsc -b packages/leaf/tsconfig.json --listFiles, even thoughleaf's own source and its declaredreferencesnever touchsdk, the file list includes about 76 files underpackages/sdk/, and errors appear forsdk's own source files:packages/sdk/src/utils/settings-crypto.ts(8,20): error TS2591: Cannot find name 'crypto'. packages/sdk/src/utils/mem-checkpoint.ts(31,14): error TS2868: Cannot find name 'Bun'.tsc -b packages/sdk/tsconfig.jsonrun standalone: exit 0, no errors. Same files, same compilerOptions, correct when built directly.
Root cause (as far as traced)
node_modules/dann-sdk is a symlink to packages/sdk (standard workspace linking, confirmed universal across npm, yarn, pnpm, and Bun). When TypeScript resolves the bare specifier "dann-sdk" from inside packages/trace/dist/kernel-log.d.ts, it walks up through node_modules, finds the symlink, and since the symlink's real path is identical to a project already known via references elsewhere in the graph, appears to fold sdk's raw source into whichever project's compilation triggered that resolution, instead of routing it through sdk's own composite project boundary. The same files compiled through their own tsconfig.json are clean.
Tried and ruled out
disableSourceOfProjectReferenceRedirect: trueon the referencing project (trace): no change.disableSourceOfProjectReferenceRedirect: trueon the referenced project (sdk, where the docs say it belongs): no change.- Explicit
pathsremapping ofdann-sdkto source across every referencing project'stsconfig.json(mirroring an existing working pattern used elsewhere in the same repo for three other packages that do not trigger this bug): no change. - Deleting all
.tsbuildinfoand forcing a full rebuild (--force): no change, rules out stale incremental cache. - Confirmed not an artifact of accumulated
dist/output from repeated test runs: reproduced from a completely fresh clone, fresh install, first ever build. - Confirmed not fixable at the package manager level: symlinked workspace members are intentional and universal across npm, yarn, pnpm, and Bun (see maintainer confirmation above). A
file:dependency kept out of theworkspacesglob installs as a real physical directory instead of a symlink and does not trigger the bug, but that gives up live workspace linking entirely, it is a workaround, not a fix.
Expected: sdk's files should only ever be type-checked under sdk's own compilerOptions, regardless of which symlinked path led TypeScript to them.
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Beginne damit, das Problem mit den Dateien packages/trace, packages/leaf und packages/sdk tsconfig.json zu reproduzieren, wobei du tsc -b und --listFiles verwendest. Untersuche, wie der Import in packages/trace/dist/kernel-log.d.ts über das symbolisch verknüpfte node_modules-Paket aufgelöst wird und wie Projektreferenzen compilerOptions auswählen. Erledigt bedeutet, dass der sdk-Quellcode beim Bauen von leaf nur unter den Optionen von sdk geprüft wird, ohne die gemeldeten ambient-global-Fehler.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- typescript
- Bereich
- compilers
- Issue-Typ
- Bug
- Schwierigkeit
- 5/5
- Geschätzter Aufwand
- Über eine Woche
- Aktivitätsstatus
- Ruhig
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 35/100