Skip to content

Root tsconfig.node.json emits build artifacts next to every source — 5,838 files, none of them gitignored #3549

Description

@yinlianghui

Observation-class finding, tripped over while implementing #3515. Nothing a user hits today — no script anywhere runs this config. Recording it because it silently corrupts the commit of whoever does run it, and #3515's own body invites exactly that: it names the root tsconfig.node.json as "the only program that does contain" the four root setup files, which is a standing invitation to go run it.

What happens

tsconfig.node.json is three lines:

{
  "extends": "./tsconfig.base.json",
  "compilerOptions": { "lib": ["ES2020"] }
}

tsconfig.base.json sets "declaration": true, "declarationMap": true, "sourceMap": true — and no noEmit, no outDir. tsconfig.node.json also declares no include, so it defaults to the whole repository. Running it once, on a clean origin/main worktree:

$ ./node_modules/.bin/tsc -p tsconfig.node.json
...21616 errors...
$ git status --porcelain | wc -l
5838

5,838 emitted .js / .js.map / .d.ts / .d.ts.map files, written next to every input across packages/, apps/, examples/, scripts/ and the repo root.

Why it is worth recording

None of the emitted shapes is gitignored:

path git check-ignore
packages/components/src/lib/utils.js NOT ignored
packages/components/src/lib/utils.d.ts NOT ignored
vitest.setup.dom.js NOT ignored
scripts/vite-crypto-stub.js NOT ignored

So the next git add -A commits all of them. That is not hypothetical — it happened during #3515: a throwaway git add -A && git commit swallowed 402,729 insertions across 5,838 files. The knock-on is worse than the noise, because the emitted .js files carry JSX (they came from .tsx inputs), and rolldown then reads src/lib/utils.js in preference to src/lib/utils.tsx:

@object-ui/components:build: Build failed with 165 errors:
@object-ui/components:build: [builtin:vite-transform] Unexpected JSX expression
@object-ui/components:build:     ╭─[ src/context/gridFieldAuthoring.js:22:13 ]
@object-ui/components:build:     Help: JSX syntax is disabled and should be enabled via the parser options

A package that built fine ten minutes earlier now fails 165 times, and the failure reads as "your change broke the build" while having nothing to do with any change. Recovering means noticing that src/*.js files exist at all.

The repo already knows this shape. apps/console/tsconfig.node.json carries a comment saying so in as many words — "Left at the default it writes .js/.d.ts next to every input (apps/console/vite.config.js, scripts/vite-*.d.ts, …), and that untracked litter is a large part of why this project was never wired into a gate (objectui#3305)". The root file of the same name never got the same treatment.

Shape of a fix (not prescribing — the options differ in blast radius)

  1. "noEmit": true. Legal here because the project is standalone — nothing references it, so TS6310 does not apply. Same argument tsconfig.scripts.json and tsconfig.vitest-setup.json each make for themselves. Cheapest, and fixes the cause.
  2. An outDir under node_modules/.cache/tsc/, mirroring what apps/console/tsconfig.node.json was forced into. More machinery than this file needs, since it has no TS6310 constraint.
  3. Add the emitted shapes to .gitignore. Weakest: it treats the symptom, and it would also hide genuinely-stray artifacts from every other source.
  4. Delete the file, if nothing uses it. Measure first — editors may be picking it up as the ambient project for root-level files, which is the only reason it plausibly still exists.

Worth noting for whoever picks this up: the same run reports 21,616 errors. That backlog is not part of this finding, and #3515 explicitly rejected "widen the root config and give it a runner" (its route 3) for exactly that reason. Fixing the emit is independent of, and much smaller than, deciding what to do about the errors.

Related: #3515 (the coverage gap next door), #3494 / PR #3498 (tsconfig.scripts.json), #3305 (apps/console's node project and its litter), #3476 / PR #3513.


Generated by Claude Code

Activity

  1. self-assigned this
    on Aug 7, 2026
  2. yinlianghui commented on Aug 7, 2026

    @yinlianghui
    CollaboratorAuthor

    认领(发现分诊轮晋级)— 会话 session_01GTRjn8xBqp75dk7kFupVRt,分支 claude/issue-3549-node-tsconfig-emit-guard。

    分诊裁定:这条不是休眠观察 —— #3515 的 dev 已实测踩爆一次(5,838 个排放物 + 165 个 rolldown 假错,读起来像"你的改动弄坏了构建"),每个未来 agent 都可能重复。修形取最小静态防线:给根 tsconfig.node.json 加 noEmit: true(该文件无任何 script 运行它,是纯编辑器/引用配置,不该有排放权)—— 若实测发现有工具链依赖其 emit,则退而求其次 .gitignore 防护 + 文件头注释警示,按实测定,报告说明取舍。逆向验证:修后手跑 tsc -p tsconfig.node.json,断言零新文件落盘(git status --porcelain 为空)。

    文件面:tsconfig.node.json(或退路 .gitignore + 注释)。越界即停。


    Generated by Claude Code

  3. yinlianghui commented on Aug 7, 2026

    @yinlianghui
    CollaboratorAuthor

    验收:ACCEPT — PR #3574。

    核过 GitHub 实物(恰 1 文件,+28/-1:一行 noEmit: true + 承重头注释)。裁定要点:

    1. 前提与逆验证都量了:修前实测 5,850 个排放物(与 issue 的 5,838 随 main 漂移,吻合)、修后 git status --porcelain 仅剩被编辑文件本身、git clean -dn 零条目。
    2. 两个预测落空的处理是本单最有价值的部分:诊断数 23903→23902(消失的恰是 TS2883 —— 仅在写 .d.ts 时产生的 emit-only 诊断)与退出码 1→2(tsc 语义:2 = 有错且未产出输出)—— dev 没有把数字捏回模板,而是逐条查出机制,两者都成了"emit 确实停了"的确证。
    3. 8 处引用逐一处置,apps/console 同名文件陷阱(派单警告过的)正确甄别;TS6310 合法性用"无任何 references 边"实测清障,并指出这正是 scripts/vitest-setup 两个兄弟配置的既有仓库模式。
    4. 头注释把 rolldown 优先解析 .js 于 .tsx 这层二次伤害也写进去了 —— 下一个想删 noEmit 的人会先读到 165 个假构建错误的来历。
    5. 偏差一(vitest-setup 门首跑红)用单文件回退实证与本改动无关(既有 ^build 前置,文档写明的),处置正确。

    PR 转 ready + auto-merge。


    Generated by Claude Code

  4. added a commit that references this issue on Aug 10, 2026
    c35fed0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions