microsoft / microsoft/TypeScript

Project references resolve a symlinked sibling's raw source instead of its own composite output, using the wrong project's compilerOptions

オープン
#63,743 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

Needs More Info
主要言語
Go
スター
111k
フォーク
14.3k
平均マージ
2日 4時間
マージ済み PR(30日)
132

説明

### Acknowledgement

- [x] #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`:

```json
{
"workspaces": ["packages/*"]
}
```

Steps:
1. Install with any workspace-aware package manager (npm, yarn, pnpm, or Bun). Local packages get symlinked into `node_modules`.
2. `tsc -b packages/trace/tsconfig.json`, succeeds, emits `packages/trace/dist/kernel-log.d.ts` containing `import("dann-sdk").LogCtx`.
3. `tsc -b packages/leaf/tsconfig.json --listFiles`, even though `leaf`'s own source and its declared `references` never touch `sdk`, the file list includes about 76 files under `packages/sdk/`, and errors appear for `sdk`'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'.
```
4. `tsc -b packages/sdk/tsconfig.json` run 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: true` on the referencing project (`trace`): no change.
- `disableSourceOfProjectReferenceRedirect: true` on the referenced project (`sdk`, where the docs say it belongs): no change.
- Explicit `paths` remapping of `dann-sdk` to source across every referencing project's `tsconfig.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 `.tsbuildinfo` and 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 the `workspaces` glob 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.

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

まず、tsc -b と --listFiles を使用して、packages/trace、packages/leaf、packages/sdk の tsconfig.json ファイルで問題を再現します。packages/trace/dist/kernel-log.d.ts 内の import がシンボリックリンクされた node_modules パッケージを介してどのように解決されるか、またプロジェクト参照がどのように compilerOptions を選択するかを調べます。leaf のビルド時に sdk のソースが sdk のオプションだけでチェックされ、報告された ambient-global エラーが発生しなければ完了です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
typescript
領域
compilers
issue の種類
バグ
難易度
5/5
見積もり時間
1週間以上
活発さ
静か
明瞭さ
おおむね明確
初心者へのやさしさ
35/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。