Uh oh!
There was an error while loading. Please reload this page.
fix(build): wire both shared vite-plugin-dts factories into every dts package, and ratchet them there - #6201
Merged
Conversation
…kages `scripts/vite-dts-fail-on-type-errors.ts` landed with #5370 / PR #5482 and was spread into `packages/layout` only, because that PR's declared file surface was the shared module plus that one package. The other 20 `vite-plugin-dts` packages kept printing a type error from the declaration program and exiting 0. Spread `createDtsFailOnTypeErrors({ packageDir: __dirname })` into all 20, in the form pinned by `packages/layout/vite.config.ts`. `packages/fields` is deliberately left unwired and now says so at the call site: its build script is `tsc && vite build && node scripts/build-css.mjs`, and the leading `tsc` exits non-zero before `vite build` ever runs — so a dts-leg exit code could not be what decides that build. The comment records that the prefix is load-bearing rather than redundant next to the 21 wired call sites. Part of #5483
…typings `scripts/vite-dts-explicit-extensions.ts` landed with #5365 and was wired into `packages/components` and `packages/layout` only, because that dispatch was fenced to those two. Every other `vite-plugin-dts` package kept shipping extensionless relative specifiers in its emitted `dist/**/*.d.ts` — 410 of them, measured on this tree. This is not "#5365 again". Those two packages re-export mostly through `export * from './ui'`, and a star re-export from a module the compiler cannot resolve contributes NO names — the consumer gets a loud TS2305. These packages re-export mostly through NAMED re-exports, so TypeScript still declares the name and merely fails to type it: the consumer gets no error at all and a silent `any`. It is the quieter and worse of the two failure modes, and it opens the moment a consumer pins `moduleResolution: nodenext`. Spread `createDtsExplicitExtensions({ packageDir: __dirname })` into the remaining 20 `dts(` call sites, in the form pinned by the two #5365 landed. Adds `scripts/__tests__/vite-dts-wiring-ratchet.test.ts`, which serves BOTH halves of this branch: one AST walk over every `vite.config.ts` that calls `dts(`, asserting both shared factories are spread. Without it each wiring is a line whose absence is invisible, which is how 1-of-22 and 2-of-22 came to exist. Part of #5439
yinlianghui-tw
marked this pull request as ready for review
August 25, 2026 01:08
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
|
yinlianghui-tw
enabled auto-merge
August 25, 2026 01:09
Uh oh!
There was an error while loading. Please reload this page.
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 freeto 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#5483
Fixes#5439
Folded dispatch, chain head #5483. Two commits, two acceptance criteria, and one ratchet over the population both cards describe — which is the reason they were folded rather than run serially. Run serially this would have produced two near-duplicate gates over one population, each checking one module.
6c768e219— 20 more vite-plugin-dts packages still print a type error and exit 0 — the shared fail-on-error factory is wired into onlypackages/layout#5483:createDtsFailOnTypeErrorsinto the remaining 20 call sites.beeb94058— 13 more vite-built packages ship extensionless typings, and their NAMED re-exports degrade toanyinstead of going missing #5439:createDtsExplicitExtensionsinto the remaining 20 call sites, plus the shared ratchet.Population, re-derived on today's
main(17ccec977)Both cards' populations were stale — #5483's from 2026-08-21, #5439's from 2026-08-20, and #5439's said so about itself. Re-derived from the tree:
vite.config.tsfiles calldts(—components,fields,layout, and 19plugin-*.createDtsFailOnTypeErrorswas wired into 1 of 22 (layout). Now 21 of 22.createDtsExplicitExtensionswas wired into 2 of 22 (components,layout). Now 22 of 22.#5439's 13-package table is not the population. The six it listed as "outside the closure built here and are untested, not clean" were all measured here and all needed wiring:
plugin-gantt17,plugin-timeline4,plugin-ai3,plugin-markdown3,plugin-map1,plugin-tree1 extensionless specifiers.plugin-detailmeasured 82, not 81.The one package that stays unwired, and why
packages/fieldskeeps itstsc &&prefix and does not spreadcreateDtsFailOnTypeErrors. Its build script istsc && vite build && node scripts/build-css.mjs; the leadingtscexits non-zero on the same diagnostics beforevite buildruns, so a dts-leg exit code could never be what decides that build. The prefix is load-bearing, not redundant next to the 21 wired call sites, and the call site now says so.The card asked for that as a comment at the build script.
package.jsoncannot hold a comment and this repo has no"//"-key convention to borrow (zero occurrences repo-wide), so the note lives at thedts()call inpackages/fields/vite.config.tsand names the build script — and the claim is machine-checked: the ratchet's exclusion entry re-derives thetsc &&prefix frompackages/fields/package.json. Drop the prefix and the gate reds, demanding the factory instead. That is strictly stronger than prose, which is why it was done this way rather than inventing a JSON comment convention across 39 manifests.fieldsdoes getcreateDtsExplicitExtensions— measured 0 extensionless both before and after, because its sources already write explicit.jsspecifiers, so wiring it changes nothing today and stops it drifting back.#5439 is not "#5365 again" — this is the whole severity argument
A PR body that describes this as "wire the extensions module into more packages" loses the reason it matters.
export * from './ui'. A star re-export from a module the compiler cannot resolve contributes no names, so the consumer gets a loudTS2305: has no exported member— a wall, hit immediately.export { useObjectChat } from './useObjectChat'. TypeScript still declares the name and merely fails to type it. The consumer gets no error at all and a silentany.Proven on
@object-ui/plugin-chatbotin #5439 by compiling the same builtdisttwice against a probe (generics spaced to survive GitHub's body sanitizer):moduleResolution: nodenext— compiles clean, i.e. the symbol isany.moduleResolution: bundler—TS2322, i.e. properly typed.That is the worse of the two failure modes: it opens quietly, the moment a consumer pins
nodenext, and it is what produces the 21 residualTS7006on@object-ui/app-shellreported in #5365's PR.Measurements
#5483 — blast radius, re-measured on today's
main: zeroturbo run build --filter=!@object-ui/site --concurrency=2 --continuewith the factory wired into 21 call sites: 43/43 tasks successful, 4m12s. None of the 22 dts packages was a cache hit (verified against the run'scache hit/cache misslines — the 14 hits aretypes,core,react,i18n,mobile,permissions,providers,auth,collaboration,sdui-parser,data-objectstack,react-runtime,create-plugin,object-ui), so all 22 built fresh. Wiring turns nothing green red.#5439 — per package, built with the module wired before the final diff
vite-dts-explicit-extensionsthrows on any specifier it cannot resolve, so this half could have redded a currently-green build. It did not: same full build with both modules wired, 43/43 successful, 3m45s, zero throws. No package had to be left unwired, so there is no finding of that kind to report.Extensionless relative specifiers in emitted
dist/**/*.d.ts(AST-located module specifiers, so JSDoc prose is excluded — the same criterion #5439 used), before → after:plugin-detailplugin-listplugin-designerplugin-reportplugin-chatbotplugin-chartsplugin-gridplugin-calendarplugin-dashboardplugin-timelineplugin-formplugin-aiplugin-kanbanplugin-markdownplugin-viewplugin-editorplugin-ganttplugin-mapplugin-tree410 → 0.
components(119 relative specifiers),layout(8) andfields(155) were already 0 and stay 0. The before column is a real rebuild, not an inference: the spread was reverted withgit checkout HEAD~1 --after the fix was committed, the tree rebuilt, and the count re-taken; the restore was proven with an emptygit diff HEAD.Verification here is deliberately not "it still compiles" — named re-exports yield a silent
any, so compiling proves nothing. It is a direct count driven to zero.The ratchet —
scripts/__tests__/vite-dts-wiring-ratchet.test.tsOne AST walk over every
vite.config.*underpackages/,apps/,examples/that callsdts(, asserting both spreads. Verdict line carries the count:Detection is by AST, not substring:
toContainis satisfied by the factory's name appearing in a comment, which is the exact shape of a call site someone disabled and explained.dtsmust also be the local name bound to thevite-plugin-dtsdefault import, so a same-named local helper cannot join the population by coincidence.Non-vacuity, each direction predicted before it was run
...createDtsExplicitExtensions(...)frompackages/layout/vite.config.ts, leaving its import in place so the miss is the spread and not the import. Predicted: exactly one test red, naming the file and the module. Observed exactly that —1 vite-plugin-dts call site(s) do not spread createDtsExplicitExtensions… - packages/layout/vite.config.ts,1 failed | 6 passed....createDtsFailOnTypeErrors(...)frompackages/plugin-grid/vite.config.ts, leaving the other factory's spread in place so the control is single-module. Observed:1 failed | 6 passed, namingpackages/plugin-grid/vite.config.tsand that module. A gate that only noticed one would be half a gate.expected 0 to be greater than or equal to 15, plus the stale-exclusion guard. The 5 that still passed are precisely the vacuously-satisfiable ones, which is why the floor assertion exists.tsc &&prefix frompackages/fields's build script. Observed: red,The reason packages/fields/vite.config.ts is exempt from createDtsFailOnTypeErrors no longer holds.Every mutation was proven on disk by anchored counts of the injected and the removed text — never an editor's exit code — and every restore by an empty
git diff HEAD.End-to-end for #5483
One deliberate
TS2322appended topackages/plugin-tree/src/index.tsx, exit code read from${PIPESTATUS[0]}as the card did, same injection both legs:packages/plugin-tree/vite.config.tspnpm --filter @object-ui/plugin-tree buildorigin/main(unwired)TS2322printed in the logError: [dts-fail-on-type-errors] the declaration build … reported 1 type error, so it must not exit 0: src/index.tsx(49,14): error TS2322Restored, proven with an empty
git diff HEAD.Gates run locally, on final head
beeb94058Gate list derived from
package.jsonand.github/workflows/, not from the dispatch. Exit codes captured by redirect before any pipe.pnpm exec vitest run scripts/(root vitest, objectui#3378)pnpm lintpnpm lint:rootpnpm type-checkpnpm type-check:scripts/:coverage/pnpm lint:coveragepnpm check:esm-specifiers,check:node-esm-load,check:published-distpnpm check:phantom-deps,check:self-import,check:entry-guardpnpm check:spec-symbols,check:action-forward-parity,check:designer-field-key-parity,check:icon-record-namespnpm check:i18n-keys,check:i18n-drift,check:skills-pathsnode scripts/check-control-bytes.mjscheck-changeset-presence/-no-major/-fixedHeavy runs were serialised through the container's shared verify lock.
Changeset
A real one, not
skip-changeset: this changes published typings.check-changeset-presencesays none is owed (no publishedsrc/changed — only build configs), which is exactly the gap worth writing through by hand. Bumps arepatch; per AGENTS.md no changeset in this repo declaresmajor.Downstream
TS2305/TS7006surfacing in consumers is the fix working, not a regression: those call sites were being handed a silentany.Scope
The
vite.config.tsfiles of both sets,fields's one clarifying comment, the ratchet and its test, and the changeset. Neither shared module's behaviour was touched — both are landed and pinned by existing call sites, and their own unit tests (scripts/__tests__/vite-dts-*.test.ts) are unchanged and green. No pre-existing type error was fixed; none surfaced.Generated by Claude Code
Generated by Claude Code