feat(fields): build and publish the style.css the package has always declared (#4059) - #4076
Merged
Merged
Conversation
…declared (#4059) `@object-ui/fields` declared `"./style.css": "./dist/index.css"` while its build was `tsc && vite build` and the package held no `.css` file at all. Every tarball through 17.3.0 shipped with zero stylesheets, so a consumer's `@import '@object-ui/fields/style.css'` failed to resolve and broke their build — the import `content/docs/guide/quick-start.md` taught. Retiring the export was measured and rejected: 155 classes exist only in this package, and 17 of them resolve `@theme` tokens declared in unpublished `@object-ui/components` source, so no consumer-side configuration can produce them. Removal would have made the field widgets permanently under-styled. The new sheet is a supplement, not a duplicate: `src/index.css` `@reference`s components' entry for its theme and emits only the utilities layer, and `scripts/build-css.mjs` subtracts every rule that package's built sheet already ships. 21.57 kB / 157 classes instead of ~180 kB of near-duplicate CSS, and it covers all 152 classes the issue measured as missing. Also extends `scripts/__tests__/package-files-exist.test.ts` with an `exports`-reality guard (packability + CSS producibility) so a stylesheet export with nothing building it cannot recur silently, and re-teaches the fields import on the three guide pages PR #4060 removed it from. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
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 #4059
Implements Delegated Ruling #2 (comment): route B, narrow variant, after ruling #1's premise was measured and falsified.
Why this route
Ruling #1 was "retire the phantom export", premised on fields' class shapes being a subset of
@object-ui/components' published utilities. Measured first, that premise is false — so removal would have ratified a permanently under-styled package rather than costing nothing. The ruling's own escape clause sent it back, and ruling #2 selected B:What was broken
packages/fields/package.jsondeclared"./style.css": "./dist/index.css"whilescripts.buildwastsc && vite buildand the package contained no.cssfile for Vite to extract. The published@object-ui/fields@17.3.0tarball contains zero.cssfiles. The subpath did not render badly — it failed to resolve, taking the consumer's build down.Nothing in this repo noticed, structurally:
apps/console,examples/console-starterandexamples/byo-backend-consoleall reach fields' source through@source '.../packages/fields/src/**', a workspace-relative path no published consumer can write. In-repo rendering never exercised the export.Implementation
packages/fields/src/index.css— a build-time-only Tailwind entry:@referencemakes components' theme tokens, its class-baseddarkvariant and its animate plugin available for resolution while emitting nothing of them — no preflight, no@theme:rootblock, no base layer.packages/fields/scripts/build-css.mjs— mirrorspackages/components/scripts/build-css.mjs, then subtracts every rule components' built sheet already ships.How the coupling to components' unpublished source is expressed
Two build-time relative paths inside the monorepo, neither reachable from the published tarball:
@reference '../../components/src/index.css'packages/components/dist/index.cssThis mirrors components' own build script's coupling to its
src/index.css, one directory further away.turbo run buildorders it correctly viadependsOn: ["^build"]; a bare single-package build fails with an actionable message namingpnpm --filter @object-ui/components build.Reading components' built artifact rather than re-compiling its entry is deliberate — see "two bugs found" below.
Result
21.57 kB instead of ~180 kB. The naive entry re-emits preflight, the theme and ~1350 utilities the consumer already has — the same objection that retired two
@sourcelines in #3884 for costing 100 kB and buying 14 selectors.Acceptance evidence
Re-ran the issue's subset comparator against the built artifact:
All 152 covered, zero uncovered. The only two overlapping names are variant markers appearing inside compound selectors (
.dark\:x:where(.dark, .dark *),.group-hover\:y:is(:where(.group):hover *)), not duplicated utility rules — so duplication is effectively nil. The delta of 157 vs 152 is 5 classes present in fields' source but tree-shaken out of thedistbundle the original measurement scanned, plus those 2 markers; a superset in the safe direction.All 17 theme-token utilities that only this build can produce are present:
bg-background/60,bg-destructive/5,bg-muted/60,bg-primary/20,border-background,border-border/40,border-muted-foreground/25,border-muted-foreground/30,focus-visible:ring-ring/60,focus:ring-ring/60,hover:bg-accent/30,hover:bg-muted/80,hover:bg-primary/10,hover:bg-primary/50,hover:border-primary/50,ring-border/60,ring-destructive/50.The export now resolves for real —
npm pack --dry-run:(
src/index.cssships becausefileslistssrc, butexportshas no subpath reaching it, so it stays encapsulated. Its header says build-time-only.)Two bugs found and fixed while building this
Both would have shipped a wrong stylesheet with a green build, so they are worth naming:
packages/fieldsand folded fields' own classes into the set being subtracted. Every fields-only rule then looked "already shipped". Fixed by reading components' builtdist/index.cssinstead — an artifact, not a re-derivation.source(none), which pins the inputs to the explicit@sourcelines. Verified byte-identical from both working directories.The build now refuses to write on four conditions: a rule went missing, nothing was subtracted, a named theme-token sentinel vanished, or the class count exceeds a generous leak-detector ceiling.
The durable gate
Extends
scripts/__tests__/package-files-exist.test.ts— the existing home for manifest discipline (objectui#3663 / #3696) — with anexports-reality section asking two questions:filesships? (@object-ui/app-shellis the live positive case: it listssrc/styles.cssinfilesprecisely so its subpath resolves.).csstarget no committed file supplies must have a producer: a CSS source undersrc/, or a CSS step in the build script. Both derived from the package's own plumbing, never a name allowlist.The objectui#3663 guard could not catch this:
dist/index.cssis git-ignored, untracked, and the package has abuildscript, so it was excused as build output — that guard says of itself "What this guard deliberately does NOT prove: that a build output is really produced." This closes exactly that gap for the one file type where the question is decidable statically..js/.d.tstargets deliberately get no producibility test; there is no honest static answer for them and the gap is named rather than guessed at.Producibility keys on git's index, not on
onDisk. That was a correction the reverse verification forced: the first version passed against a tree wheredist/index.csswas sitting there from a local build — green on the very state it exists to reject.Reverse verification
Predicted direction: red on the pre-fix state, in both build states (the second is the one the
onDiskversion got wrong).dist/index.cssabsentdist/index.csspresentdist/index.csspresentdist/index.cssabsentThe failure message is the defect verbatim:
Taken out with
git checkout origin/main -- PATHand file moves — nevergit stash.Docs
Re-teaches the fields import on the three pages PR #4060 removed it from (quick-start Step 3, theming §Tailwind Setup, troubleshooting §2), each stating the import order and that the fields sheet is a supplement that styles nothing on its own.
quick-start.md:60's "you do not add@sourcelines for the ObjectUI packages" is kept but repaired: it was measured true for components and generalized across the package boundary, where it was false for fields. With this change it is true for both, which is what makes it keepable rather than deletable.The troubleshooting parenthetical that told readers "its published package contains no stylesheet … Do not add it" is replaced with the version fact — releases through 17.3.0 shipped none, so on those versions the import still fails and the fix is to upgrade, not to add scanning paths.
Changeset
minor, argued from the never-worked fact: this is the first release in which the import resolves at all, so it is a new capability rather than a repaired one, and an import that never resolved can have no working consumers to break. Notmajorper AGENTS.md §202-206 (the fixed group must track@objectstack's major). Bump Policy check arbitrates.Verification
pnpm --filter @object-ui/fields builddist/index.css21.57 kBnpx vitest run packages/fields --maxWorkers=2npx vitest run scripts/__tests__ --maxWorkers=2pnpm --filter @object-ui/fields type-checkpnpm --filter @object-ui/fields lintpnpm type-check:scriptscheck-lint-coverage/check-type-check-coveragecheck-changeset-presence/-no-major/-fixedcheck-control-bytes+ targeted self-scancheck-doc-linksScope
Region exclusivity vs the in-flight #3569 branch respected: no
GridField*or other source component file touched. Thepackages/fieldssurface is itspackage.json, a newsrc/index.cssand a newscripts/build-css.mjs— the CSS entry the ruling explicitly put in scope.Generated by Claude Code