fix(devx): pin tsBuildInfoFile outside the published dist in every composite package - #9328
Merged
Merged
Conversation
…mposite package `composite` implies `incremental`, so every composite project writes a `*.tsbuildinfo`. Where it lands is DERIVED, not declared: with an `outDir` and no `rootDir`, TypeScript puts the record at `outDir/<configname>.tsbuildinfo` — inside `dist`, which `files: ["dist", …]` publishes whole. `check:published-dist` refuses a record there (objectui#7003), so its verdict came to depend on WHEN it ran: green before anything invoked `tsc`, red after, on a byte-identical tree. Measured at c64975e with TypeScript's own `getTsBuildInfoEmitOutputFilePath`: 18 of this repository's 32 incremental projects derived their record into `dist`, not the 2 the report named. The discriminator is `rootDir`, not the build tool — the 12 correct ones carry `rootDir: "src"`, which rebases the derived path back out of `outDir`, and `packages/components/tsconfig.json` records in its own comment that `rootDir` was removed there for an unrelated reason. Every composite package now pins the record to its own package root, so the location is a decision rather than a side effect of an unrelated option. A shared setting in the base config was measured and rejected: TypeScript resolves a relative `tsBuildInfoFile` against the config that DECLARES it, so one line in `tsconfig.json` collapses all 30 packages onto a single repo-root record. `clean` in `data-objectstack` and `plugin-designer` now removes the relocated record too, matching the five packages that already spell it that way — without it, `rm -rf dist` would leave a record describing files that no longer exist (objectui#6703). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Base merge only, to clear `mergeable_state: behind`. No conflict: objectui#9320 / #9321 / #9322 touch no tsconfig and no file this branch changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
baozhoutao
marked this pull request as ready for review
September 13, 2026 02:19
baozhoutao
deleted the
claude/9189-tsbuildinfo-outside-published-dist
branch
September 13, 2026 02:37
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #9189
The defect
compositeimpliesincremental, so every composite project writes a*.tsbuildinfo. Where it lands is derived, not declared: with anoutDirand norootDir, TypeScript puts the record atoutDir/CONFIGNAME.tsbuildinfo— insidedist, whichfiles: ["dist", ...]publishes whole.pnpm check:published-distrefuses a record there (objectui#7003), so the gate's verdict came to depend on when it ran: green before anything invokedtsc, red after, on a byte-identical tree.That is the card's sharpest sentence and triage promoted it to the basis of the ruling: ordering decides the verdict, and nothing announces it.
Two premises measured FALSE — please read this before judging the diff size
Both rulings are implemented as written. But the reasons given for them did not survive measurement, and one of them changes what the fix has to be.
1. The population is 18, not 2 — and the discriminator is
rootDir, not the build tool.The card and triage both explain the two visible packages as "the two whose
buildisvite buildrather thantsc, leavingdistotherwise free of tsc output", with the others hidden by "coincidental masking". Measured with TypeScript's owngetTsBuildInfoEmitOutputFilePathatc64975e9f: 18 of this repository's 32 incremental projects derived their record intodist. The 12 correct ones are correct because they carryrootDir: "src", which rebases the derived path back out ofoutDir. Nothing is masking anything — the other 16 simply had not been type-checked in that tree.Reproduced directly: running
tsc --noEmitin@object-ui/plugin-charts, a package the card never mentions, reds the same gate.This strengthens the scope ruling rather than weakening it.
packages/components/tsconfig.jsonstates in its own comment thatrootDirwas removed there for an unrelated reason — and that removal is exactly what moved its record into the published directory. So the 12 "already correct" packages are one unrelated edit away from joining the 18. Pinning all 30 is what makes the location a decision instead of a side effect.2. The shared base tsconfig cannot carry this setting.
The ruling preferred a shared base if one exists. Package tsconfigs extend the root
tsconfig.json, so I measured it: TypeScript resolves a relativetsBuildInfoFileagainst the config that declares it, not the one that inherits it. One line in the root config collapses all 30 packages onto a single repo-roottsconfig.tsbuildinfo— every package overwriting every other package's incremental record.So the ruling's stated fallback — "one line per package is an acceptable landing" — is not a shortcut here; it is the only spelling that gives each package its own record. (
tsconfig.base.jsonexists but no package tsconfig extends it, which is worth knowing separately.)What changed
packages/*/tsconfig.json—"tsBuildInfoFile": "./tsconfig.tsbuildinfo", one line plus the shared note. For the 12 that already landed there viarootDir, this is a no-op in effect and pure explicitness; their derived paths are byte-identical before and after.cleanscripts (data-objectstack,plugin-designer) — nowrm -rf dist tsconfig.tsbuildinfo, matching the five packages that already spell it that way. Without this, moving the record out ofdistwould leaverm -rf distdeleting the outputs while keeping the record that says they exist — the objectui#6703 desync, re-opened by this very change.scripts/__tests__/tsbuildinfo-outside-published-dist-9189.test.ts. It asks TypeScript where each record goes rather than grepping for the option, because a presence-grep would pass on a value still pointing intodistand red on a project that is correct viarootDir.BUILD_OUTPUT_DIRSis imported from the gate rather than re-spelled.tsconfig.jsonis not in any package'sfileslist, andcheck-changeset-presence.mjsindependently reports0 of them published source.Not a tsconfig refactor: no other option moved, no config was restructured. This belongs to objectui#7212's population and does not attempt to solve it.
The card's probe, run in order, exit codes captured before any pipe
The post-fix run is deliberately stronger than the card's: instead of type-checking one package, it type-checks every package, then asks the gate. Records inside any
distafterwards: 0.The tarball is unchanged.
6013 tarball file(s), 5857 of them build output, 0 tooling artifact(s)on the pre-fix green run and identically on the post-fix green run; the red run in between differed by exactly the two records (6015 / 5859 / 2).Control — preserved
@object-ui/core,@object-ui/reactand@object-ui/fieldscarry no record indisttoday and sprout nothing new:@object-ui/corepackages/core/tsconfig.tsbuildinfopackages/core/tsconfig.tsbuildinfo(unchanged)@object-ui/reactpackages/react/tsconfig.tsbuildinfopackages/react/tsconfig.tsbuildinfo(unchanged)@object-ui/fieldstsconfig.jsonis not composite at allNew location, confirmed by re-running the gate
Every record now lands at
packages/PKG/tsconfig.tsbuildinfo. The proof is the gate, not the path: the post-fix run above was made with all 31 records present on disk and reported0 tooling artifact(s) in build outputand nosrc-tierline — that second channel is how this gate reports a build record shipping from outside a build output directory, so its silence is the positive statement that the new home ships nowhere.Not committable at its new home
git check-ignore --verboseon all 31 records on disk: 31 of 31 ignored, by.gitignore:36:*.tsbuildinfo. Positive control:git check-ignore packages/components/tsconfig.jsonexits 1, so the check can distinguish.git status --porcelainshows 0 records.A pre-existing tree carries a stale record at the old path; measured, the next
vite buildemptiesdistand takes it, so there is no migration step.Ablation — the pin test can fail
Removing the pin from
packages/plugin-detail/tsconfig.jsonon disk (mutation proved landed: occurrences 1 to 0, blob7bfdd4e2to4c56da45) turns it red naming the right file, and restoring viagit checkout HEAD -- ...returns the blob to7bfdd4e2withgit diff HEADempty:Gates run, with exit codes
pnpm exec turbo run buildpnpm exec turbo run type-checkpnpm run type-check:scriptsscripts/project;--listFilesconfirms the new test is in its programpnpm run check:published-distpnpm run check:published-tsconfig-excludepnpm run check:dist-completenesspnpm run check:control-bytespnpm run check:test-path-rootspnpm run check:new-line-citationspnpm run check:self-importpnpm run check:phantom-depscheck-changeset-presence/-no-major/check:changeset-claimscheck-governed-queue-guard --testpnpm exec vitest run scripts/pnpm exec vitest run(5 affected packages)pnpm exec eslint --no-inline-config(34 changed files)Repo-wide
pnpm testandpnpm lintare declared to CI.🤖 Generated with Claude Code
https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
Generated by Claude Code